$strimportgroups = get_string("importgroups");
$this->set_upload_manager(new upload_manager('userfile', true, false, '', false, $maxuploadsize, true, true));
- $this->set_max_file_size('', $maxuploadsize);
+ //$this->set_max_file_size('', $maxuploadsize);
$mform->addElement('header', 'general', '');//fill in the data depending on page params
//later using set_defaults
--- /dev/null
+<?php
+$string['addmoreqblanks'] = '{no} More Sets of Blanks';
+$string['notenoughquestions'] = 'You must supply at least $a question and answer pairs.';
+$string['nomatchinganswerforq'] = 'You must specify an answer for this question.';
+?>
\ No newline at end of file
-<?PHP // $Id$
+<?PHP // $Id$
// qtype_multichoice.php - created with Moodle 1.7 beta + (2006101003)
+$string['addmorechoiceblanks'] = 'Blanks for {no} More Choices';
$string['answerhowmany'] = 'One or multiple answers?';
$string['answersingleno'] = 'Multiple answers allowed';
$string['answersingleyes'] = 'One answer only';
$string['choiceno'] = 'Choice $a';
$string['choices'] = 'Available choices';
$string['clozeaid'] = 'Enter missing word';
+$string['correctfeedback'] = 'For any correct answer';
$string['editingmultichoice'] = 'Editing a Multiple Choice question';
$string['feedback'] = 'Feedback';
$string['fillouttwochoices'] = 'You must fill out at least two choices. Choices left blank will not be used.';
$string['fractionsaddwrong'] = 'The positive grades you have chosen do not add up to 100%%<br />Instead, they add up to $a%%<br />Do you want to go back and fix this question?';
$string['fractionsnomax'] = 'One of the answers should be 100%%, so that it is<br />possible to get a full grade for this question.<br />Do you want to go back and fix this question?';
+$string['incorrectfeedback'] = 'For any incorrect answer';
$string['notenoughanswers'] = 'This type of question requires at least $a answers';
+$string['overallfeedback'] = 'Overall Feedback';
$string['overallcorrectfeedback'] = 'Feedback for any correct answer';
$string['overallincorrectfeedback'] = 'Feedback for any incorrect answer';
$string['overallpartiallycorrectfeedback'] = 'Feedback for any partially correct answer';
+$string['partiallycorrectfeedback'] = 'For any partially correct answer';
$string['shuffleanswers'] = 'Shuffle answers';
$string['singleanswer'] = 'Choose one answer.';
--- /dev/null
+<?php
+$string['addmoreanswerblanks'] = 'Blanks for {no} More Answers';
+$string['answerno'] = 'Answer $a';
+$string['filloutoneanswer'] = 'You must provide at least one possible answer. Answers left blank will not be used. \'*\' can be used as a wildcard to match any characters. The first matching answer will be used to determine the score and feedback.';
+?>
\ No newline at end of file
$title = optional_param("title$i", '', PARAM_NOTAGS);
$module = optional_param("module$i", 'moodle', PARAM_ALPHAEXT);
$func[$i] = 'helpbutton';
- $topics[$i] = array($keyword, $title, $module);
+ $topics[$i] = helplink($keyword, $title, $module);
}
}
* @param string $repeathiddenname name for hidden element storing no of repeats in this form
* @param string $addfieldsname name for button to add more fields
* @param int $addfieldsno how many fields to add at a time
- * @param array $addstring array of params for get_string for name of button, $a is no of fields that
- * will be added.
+ * @param string $addstring name of button, {no} is replaced by no of blanks that will be added.
* @return int no of repeats of element in this page
*/
- function repeat_elements($elementobjs, $repeats, $options, $repeathiddenname, $addfieldsname, $addfieldsno=5, $addstring=array('addfields', 'form')){
+ function repeat_elements($elementobjs, $repeats, $options, $repeathiddenname, $addfieldsname, $addfieldsno=5, $addstring=null){
+ if ($addstring===null){
+ $addstring = get_string('addfields', 'form', $addfieldsno);
+ } else {
+ $addstring = str_ireplace('{no}', $addfieldsno, $addstring);
+ }
$repeats = optional_param($repeathiddenname, $repeats, PARAM_INT);
$addfields = optional_param($addfieldsname, '', PARAM_TEXT);
if (!empty($addfields)){
$mform->setConstants(array($repeathiddenname=>$repeats));
for ($i=0; $i<$repeats; $i++) {
foreach ($elementobjs as $elementobj){
- $elementclone=clone($elementobj);
+ $elementclone = clone($elementobj);
$name=$elementclone->getName();
$elementclone->setName($name."[$i]");
if (is_a($elementclone, 'HTML_QuickForm_header')){
$value=$elementclone->_text;
- $elementclone->setValue($value.' '.($i+1));
+ $elementclone->setValue(str_replace('{no}', ($i+1), $value));
+
+ } else {
+ $value=$elementclone->getLabel();
+ $elementclone->setLabel(str_replace('{no}', ($i+1), $value));
}
$mform->addElement($elementclone);
}
}
}
- $mform->addElement('submit', $addfieldsname, get_string('addfields', 'form', $addfieldsno));
+ $mform->addElement('submit', $addfieldsname, $addstring);
$mform->closeHeaderBefore($addfieldsname);
*/
function MoodleQuickForm($formName, $method, $action, $target='', $attributes=null){
global $CFG;
+ static $formcounter = 1;
HTML_Common::HTML_Common($attributes);
$target = empty($target) ? array() : array('target' => $target);
$this->_formName = $formName;
//no 'name' atttribute for form in xhtml strict :
- $attributes = array('action'=>$action, 'method'=>$method, 'id'=>strtr($formName, '_', '-')) + $target;
+ $attributes = array('action'=>$action, 'method'=>$method, 'id'=>'mform'.$formcounter) + $target;
+ $formcounter++;
$this->updateAttributes($attributes);
//this is custom stuff for Moodle :
function getLockOptionEndScript(){
$js = '<script type="text/javascript">'."\n";
$js .= '//<![CDATA['."\n";
- $js .= "var ".$this->_formName."items= {";
+ $js .= "var ".$this->getAttribute('id')."items= {";
foreach ($this->_dependencies as $dependentOn => $elements){
$js .= "'$dependentOn'".' : {dependents :[';
foreach ($elements as $element){
}
function lockoptionsall(formid) {
var lock = new Object();
- var varname = formid.replace(/\-/g, '_');
- var items = eval(varname+'items');
+ var items = eval(formid+'items');
var form = document.forms[formid];
for (var master in items){
var subitems=items[master].dependents;
*/
define("QUESTION_NUMANS", "10");
+/**
+ * Constant determines the number of answer boxes supplied in the editing
+ * form for multiple choice and similar question types to start with, with
+ * the option of adding QUESTION_NUMANS_ADD more answers.
+ */
+define("QUESTION_NUMANS_START", 3);
+
+/**
+ * Constant determines the number of answer boxes to add in the editing
+ * form for multiple choice and similar question types when the user presses
+ * 'add form fields button'.
+ */
+define("QUESTION_NUMANS_ADD", 3);
+
/**
* The options used when popping up a question preview window in Javascript.
*/
* String in the format "'type1','type2'" that can be used in SQL clauses like
* "WHERE q.type IN ($QTYPE_MANUAL)".
*/
-$QTYPE_MANUAL = '';
+$QTYPE_MANUAL = '';
/**
* String in the format "'type1','type2'" that can be used in SQL clauses like
* "WHERE q.type NOT IN ($QTYPE_EXCLUDE_FROM_RANDOM)".
/**
* Add a new question type to the various global arrays above.
- *
+ *
* @param object $qtype An instance of the new question type class.
*/
function question_register_questiontype($qtype) {
global $QTYPES, $QTYPE_MENU, $QTYPE_MANUAL, $QTYPE_EXCLUDE_FROM_RANDOM;
-
+
$name = $qtype->name();
$QTYPES[$name] = $qtype;
$menuname = $qtype->menu_name();
require_once("$CFG->dirroot/question/type/questiontype.php");
// Load the questiontype.php file for each question type
-// These files in turn call question_register_questiontype()
+// These files in turn call question_register_questiontype()
// with a new instance of each qtype class.
$qtypenames= get_list_of_plugins('question/type');
foreach($qtypenames as $qtypename) {
}
-/**
+/**
* Returns list of 'allowed' grades for grade selection
* formatted suitably for dropdown box function
* @return object ->gradeoptionsfull full array ->gradeoptions +ve only
}
else {
return false;
- }
+ }
}
/**
* Tests whether a category is in use by any activity module
*
* @return boolean
- * @param integer $categoryid
+ * @param integer $categoryid
* @param boolean $recursive Whether to examine category children recursively
*/
function question_category_isused($categoryid, $recursive = false) {
*/
function delete_question($questionid) {
global $QTYPES;
-
+
// Do not delete a question if it is used by an activity module
if (count(question_list_instances($questionid))) {
return;
if ($states = get_records('question_states', 'question', $questionid)) {
$stateslist = implode(',', array_keys($states));
-
+
// delete questiontype-specific data
foreach ($QTYPES as $qtype) {
$qtype->delete_states($stateslist);
}
}
}
-
+
// Finally delete the question record itself
delete_records('question', 'id', $questionid);
/**
* Private function to factor common code out of get_question_options().
- *
+ *
* @param object $question the question to tidy.
- * @return boolean true if successful, else false.
+ * @return boolean true if successful, else false.
*/
function _tidy_question(&$question) {
global $QTYPES;
*
* Can be called either with an array of question objects or with a single
* question object.
- *
+ *
* @param mixed $questions Either an array of question objects to be updated
* or just a single question object
* @return bool Indicates success or failure.
// Update the state with the new response
$actions[$quid]->responses[$key] = $response;
-
+
// Set the timestamp
$actions[$quid]->timestamp = $time;
}
$states = array_values($states);
// Subtract the grade for the latest state from $attempt->sumgrades to get the
- // sumgrades for the attempt without this question.
+ // sumgrades for the attempt without this question.
$attempt->sumgrades -= $states[count($states)-1]->grade;
// Initialise the replaystate
}
$replaystate->id = $states[$j]->id;
- $replaystate->changed = true;
+ $replaystate->changed = true;
$replaystate->update = true; // This will ensure that the existing database entry is updated rather than a new one created
save_question_session($question, $replaystate);
}
if (question_isgradingevent($action->event)) {
$state->responses = $state->last_graded->responses;
}
-
+
// Check for unchanged responses (exactly unchanged, not equivalent).
// We also have to catch questions that the student has not yet attempted
- $sameresponses = !$state->last_graded->event == QUESTION_EVENTOPEN &&
+ $sameresponses = !$state->last_graded->event == QUESTION_EVENTOPEN &&
$QTYPES[$question->qtype]->compare_responses($question, $action, $state);
// If the response has not been changed then we do not have to process it again
} else { // grading event
- // Unless the attempt is closing, we want to work out if the current responses
- // (or equivalent responses) were already given in the last graded attempt.
+ // Unless the attempt is closing, we want to work out if the current responses
+ // (or equivalent responses) were already given in the last graded attempt.
if(QUESTION_EVENTCLOSE != $action->event && QUESTION_EVENTOPEN != $state->last_graded->event &&
$QTYPES[$question->qtype]->compare_responses($question, $state, $state->last_graded)) {
$state->event = QUESTION_EVENTDUPLICATE;
/**
* Private method, only for the use of add_indented_names().
- *
+ *
* Recursively adds an indentedname field to each category, starting with the category
- * with id $id, and dealing with that category and all its children, and
+ * with id $id, and dealing with that category and all its children, and
* return a new array, with those categories in the right order.
*
- * @param array $categories an array of categories which has had childids
+ * @param array $categories an array of categories which has had childids
* fields added by flatten_category_tree(). Passed by reference for
* performance only. It is not modfied.
* @param int $id the category to start the indenting process from.
* @return array a new array of categories, in the right order for the tree.
*/
function flatten_category_tree(&$categories, $id, $depth = 0) {
-
+
// Indent the name of this category.
$newcategories = array();
$newcategories[$id] = $categories[$id];
$newcategories[$id]->indentedname = str_repeat(' ', $depth) . $categories[$id]->name;
-
+
// Recursively indent the children.
foreach ($categories[$id]->childids as $childid) {
$newcategories = $newcategories + flatten_category_tree($categories, $childid, $depth + 1);
}
-
+
// Remove the childids array that were temporarily added.
unset($newcategories[$id]->childids);
-
+
return $newcategories;
}
/**
* Format categories into an indented list reflecting the tree structure.
- *
+ *
* @param array $categories An array of category objects, for example from the.
* @return array The formatted list of categories.
*/
function add_indented_names($categories) {
- // Add an array to each category to hold the child category ids. This array will be removed
+ // Add an array to each category to hold the child category ids. This array will be removed
// again by flatten_category_tree(). It should not be used outside these two functions.
foreach (array_keys($categories) as $id) {
$categories[$id]->childids = array();
}
// Build the tree structure, and record which categories are top-level.
- // We have to be careful, because the categories array may include published
+ // We have to be careful, because the categories array may include published
// categories from other courses, but not their parents.
$toplevelcategoryids = array();
foreach (array_keys($categories) as $id) {
/**
* Output a select menu of question categories.
- *
+ *
* Categories from this course and (optionally) published categories from other courses
- * are included. Optionally, only categories the current user may edit can be included.
+ * are included. Optionally, only categories the current user may edit can be included.
*
* @param integer $courseid the id of the course to get the categories for.
* @param integer $published if true, include publised categories from other courses.
*/
function question_category_select_menu($courseid, $published = false, $only_editable = false, $selected = "") {
global $CFG;
-
+
// get sql fragment for published
$publishsql="";
if ($published) {
}
$categories = get_records_sql("
- SELECT cat.*, c.shortname AS coursename
+ SELECT cat.*, c.shortname AS coursename
FROM {$CFG->prefix}question_categories cat, {$CFG->prefix}course c
WHERE c.id = cat.course AND (cat.course = $courseid $publishsql)
ORDER BY cat.parent, cat.sortorder, cat.name ASC");
$parent = $category->id;
}
else {
- // create the new category
+ // create the new category
$category = new object;
$category->course = $courseid;
$category->name = $catname;
/**
* Get list of available import or export formats
* @param string $type 'import' if import list, otherwise export list assumed
- * @return array sorted list of import/export formats available
+ * @return array sorted list of import/export formats available
**/
function get_import_export_formats( $type ) {
//-------------------------------------------------------------------------------
$repeatarray=array();
- $repeatarray[] = &MoodleQuickForm::createElement('header', '', get_string('choice','choice'));
+ $repeatarray[] = &MoodleQuickForm::createElement('header', '', get_string('choice','choice').' {no}');
$repeatarray[] = &MoodleQuickForm::createElement('text', 'option', get_string('choice','choice'));
$repeatarray[] = &MoodleQuickForm::createElement('text', 'limit', get_string('limit','choice'));
$repeatarray[] = &MoodleQuickForm::createElement('hidden', 'optionid', 0);
//-------------------------------------------------------------------------------
$this->standard_coursemodule_elements();
//-------------------------------------------------------------------------------
- $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'quiz'));
+ $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'quiz').' {no}');
$mform->setHelpButton('overallfeedbackhdr', array('overallfeedback', get_string('overallfeedback', 'quiz'), 'quiz'));
$mform->addElement('static', 'gradeboundarystatic1', get_string('gradeboundary', 'quiz'), '100%');
require_capability('moodle/question:manage', $coursecontext);
// Create the question editing form.
-$mform = $QTYPES[$question->qtype]->create_editing_form($submiturl);
+$mform = $QTYPES[$question->qtype]->create_editing_form($submiturl, $question, $category->course);
if ($mform === null) {
print_error('missingimportantcode', 'question', $returnurl, 'question editing form definition');
}
}
$question = $QTYPES[$qtype]->save_question($question, $data, $COURSE);
- $strsaved = get_string('changessaved');
if (optional_param('inpopup', 0, PARAM_BOOL)) {
- notify($strsaved, '');
+ notify(get_string('changessaved'), '');
close_window(3);
} else {
- redirect($SESSION->returnurl, $strsaved);
+ redirect($SESSION->returnurl);
}
} else {
// Display the question editing form
get_string("editquestions", "quiz").'</a> -> '.$streditingquestion;
}
print_header_simple($streditingquestion, '', $strediting);
+
+ print_heading_with_help(get_string("editing".$question->qtype, "quiz"), $question->qtype, "quiz");
+
$mform->set_defaults($question);
$mform->display();
print_footer($COURSE);
--- /dev/null
+<?php
+/**
+ * Defines the editing form for the calculated question type.
+ *
+ * @copyright © 2007 Jamie Pratt
+ * @author Jamie Pratt me@jamiep.org
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package questions
+ */
+
+/**
+ * calculated editing form definition.
+ */
+class question_edit_calculated_form extends question_edit_form {
+ /**
+ * Add question-type specific form fields.
+ *
+ * @param object $mform the form being built.
+ */
+ function definition_inner(&$mform) {
+ $mform->addElement('text', 'answers[0]', get_string("correctanswerformula", "quiz"));
+ $mform->setType('answers[0]', PARAM_NOTAGS);
+
+ $mform->addElement('hidden', 'fraction[0]', '1.0');
+
+ $mform->addElement('text', 'tolerance[0]', get_string("tolerance", "quiz"));
+ $mform->setType('tolerance[0]', PARAM_NOTAGS);
+
+ $mform->addElement('select', 'shuffleanswers', get_string('shuffleanswers', 'qtype_calculated') );
+ $mform->setHelpButton('shuffleanswers', array('calculatedshuffle', get_string('shuffleanswers','qtype_calculated'), 'quiz'));
+
+/* $mform->addElement('static', 'answersinstruct', get_string('choices', 'qtype_calculated'), get_string('fillouttwochoices', 'qtype_calculated'));
+ $mform->closeHeaderBefore('answersinstruct');
+*/
+ $creategrades = get_grade_options();
+ $gradeoptions = $creategrades->gradeoptionsfull;
+ $repeated = array();
+ $repeated[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_calculated', '{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'));
+
+ 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);
+ $repeatedoptions = array();
+ $repeatedoptions['answer']['type'] = PARAM_TEXT;
+ $repeatedoptions['fraction']['default'] = 0;
+ $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', QUESTION_NUMANS_ADD, get_string('addmorechoiceblanks', 'qtype_calculated'));
+
+ $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_calculated'));
+
+ $mform->addElement('htmleditor', 'correctfeedback', get_string('correctfeedback', 'qtype_calculated'));
+ $mform->setType('correctfeedback', PARAM_RAW);
+
+ $mform->addElement('htmleditor', 'partiallycorrectfeedback', get_string('partiallycorrectfeedback', 'qtype_calculated'));
+ $mform->setType('partiallycorrectfeedback', PARAM_RAW);
+
+ $mform->addElement('htmleditor', 'incorrectfeedback', get_string('incorrectfeedback', 'qtype_calculated'));
+ $mform->setType('incorrectfeedback', PARAM_RAW);
+
+ }
+
+ function set_defaults($question) {
+ if (isset($question->options)){
+ $answers = $question->options->answers;
+ if (count($answers)) {
+ $key = 0;
+ foreach ($answers as $answer){
+ $default_values['answer['.$key.']'] = $answer->answer;
+ $default_values['fraction['.$key.']'] = $answer->fraction;
+ $default_values['feedback['.$key.']'] = $answer->feedback;
+ $key++;
+ }
+ }
+ $default_values['single'] = $question->options->single;
+ $default_values['shuffleanswers'] = $question->options->shuffleanswers;
+ $default_values['correctfeedback'] = $question->options->correctfeedback;
+ $default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback;
+ $default_values['overallincorrectfeedback'] = $question->options->overallincorrectfeedback;
+ $question = (object)((array)$question + $default_values);
+ }
+ parent::set_defaults($question);
+ }
+
+ function qtype() {
+ return 'calculated';
+ }
+
+ function validation($data){
+ $errors = array();
+ $answers = $data['answer'];
+ $answercount = 0;
+ foreach ($answers as $answer){
+ $trimmedanswer = trim($answer);
+ if (!empty($trimmedanswer)){
+ $answercount++;
+ }
+ }
+ if ($answercount==0){
+ $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_calculated', 2);
+ $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_calculated', 2);
+ } elseif ($answercount==1){
+ $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_calculated', 2);
+
+ }
+ return $errors;
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Defines the editing form for the description question type.
+ *
+ * @copyright © 2007 Jamie Pratt
+ * @author Jamie Pratt me@jamiep.org
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package questions
+ */
+
+/**
+ * description editing form definition.
+ */
+class question_edit_description_form extends question_edit_form {
+ /**
+ * Add question-type specific form fields.
+ *
+ * @param MoodleQuickForm $mform the form being built.
+ */
+ function definition_inner(&$mform) {
+ //don't need these default elements :
+ $mform->removeElement('defaultgrade');
+ $mform->removeElement('penalty');
+
+ $mform->addElement('hidden', 'defaultgrade', 0);
+ }
+
+
+ function qtype() {
+ return 'description';
+ }
+
+
+}
+?>
\ No newline at end of file
* method.
*/
class question_edit_form extends moodleform {
+ /**
+ * Question object with options and answers already loaded by get_question_options
+ * Be careful how you use this it is needed sometimes to set up the structure of the
+ * form in definition_inner but data is always loaded into the form with set_defaults.
+ *
+ * @var object
+ */
+ var $question;
+ /**
+ * Course id
+ *
+ * @var integer
+ */
+ var $courseid;
+ function question_edit_form($submiturl, $question){
+ $this->question = $question;
+ parent::moodleform($submiturl);
+ }
/**
* Build the form definition.
*
* override this method and remove the ones you don't want with $mform->removeElement().
*/
function definition() {
- global $COURSE;
+ global $COURSE, $CFG;
$qtype = $this->qtype();
$langfile = "qtype_$qtype";
$mform =& $this->_form;
- $renderer =& $mform->defaultRenderer();
// Standard fields at the start of the form.
- $mform->addElement('header', 'formheader', get_string("editing$qtype", $langfile));
- $mform->setHelpButton('formheader', array($qtype, get_string($qtype, $qtype), $qtype));
+ $mform->addElement('header', 'generalheader', get_string("general", 'form'));
- $mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
+ /*$mform->addElement('questioncategory', 'category', get_string('category', 'quiz'),
array('courseid' => $COURSE->id, 'published' => true, 'only_editable' => true));
-
+*/
$mform->addElement('text', 'name', get_string('questionname', 'quiz'),
array('size' => 50));
- $mform->setType('name', PARAM_MULTILANG);
+ $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));
$mform->setType('questiontext', PARAM_RAW);
- $mform->setHelpButton('questiontext', array('questiontext', get_string('questiontext', 'quiz'), 'quiz'));
+ $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);
+ foreach ($coursefiles as $filename) {
+ if (mimeinfo("icon", $filename) == "image.gif") {
+ $images["$filename"] = $filename;
+ }
+ }
if (empty($images)) {
$mform->addElement('static', 'image', get_string('imagedisplay', 'quiz'), get_string('noimagesyet'));
} else {
$images[''] = get_string('none');
$mform->addElement('select', 'image', get_string('imagedisplay', 'quiz'), $images);
- $mform->setType('image', PARAM_FILE);
}
$mform->addElement('text', 'defaultgrade', get_string('defaultgrade', 'quiz'),
$mform->addElement('hidden', 'versioning');
$mform->setType('versioning', PARAM_BOOL);
- $buttonarray = array();
- $buttonarray[] = &$mform->createElement('submit', 'submitbutton', get_string('savechanges'));
- if (!empty($id)) {
- $buttonarray[] = &$mform->createElement('submit', 'makecopy', get_string('makecopy', 'quiz'));
- }
- $buttonarray[] = &$mform->createElement('reset', 'resetbutton', get_string('revert'));
- $buttonarray[] = &$mform->createElement('cancel');
- $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
- $renderer->addStopFieldsetElements('buttonar');
+ $this->add_action_buttons();
}
/**
function set_defaults($question) {
global $QTYPES;
- $QTYPES[$question->qtype]->set_default_options($question);
+ if (!isset($question->id)){
+ $QTYPES[$question->qtype]->set_default_options($question);
+ }
+ if (empty($question->image)){
+ unset($question->image);
+ }
parent::set_defaults($question);
}
--- /dev/null
+<?php
+/**
+ * Defines the editing form for the essay question type.
+ *
+ * @copyright © 2007 Jamie Pratt
+ * @author Jamie Pratt me@jamiep.org
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package questions
+ */
+
+/**
+ * essay editing form definition.
+ */
+class question_edit_essay_form extends question_edit_form {
+ /**
+ * Add question-type specific form fields.
+ *
+ * @param MoodleQuickForm $mform the form being built.
+ */
+ function definition_inner(&$mform) {
+ $mform->addElement('htmleditor', 'feedback', print_string("feedback", "quiz"));
+ $mform->setType('feedback', PARAM_RAW);
+
+ $mform->addElement('hidden', 'fraction', 0);
+ }
+
+ function set_defaults($question) {
+ if (isset($question->options)){
+ $question->feedback = $question->options->answer->feedback;
+ }
+ parent::set_defaults($question);
+ }
+
+ function qtype() {
+ return 'essay';
+ }
+
+
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Defines the editing form for the match question type.
+ *
+ * @copyright © 2007 Jamie Pratt
+ * @author Jamie Pratt me@jamiep.org
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package questions
+ */
+
+/**
+ * match editing form definition.
+ */
+class question_edit_match_form extends question_edit_form {
+ /**
+ * Add question-type specific form fields.
+ *
+ * @param object $mform the form being built.
+ */
+ function definition_inner(&$mform) {
+ $mform->addElement('selectyesno', 'shuffleanswers', get_string('shuffle', 'quiz'));
+ $mform->setHelpButton('shuffleanswers', array('matchshuffle', get_string('shuffle','quiz'), 'quiz'));
+
+ $mform->addElement('static', 'answersinstruct', get_string('choices', 'quiz'), get_string('filloutthreequestions', 'quiz'));
+ $mform->closeHeaderBefore('answersinstruct');
+
+ $repeated = array();
+ $repeated[] =& $mform->createElement('header', 'choicehdr', get_string('questionno', 'quiz', '{no}'));
+ $repeated[] =& $mform->createElement('textarea', 'subquestions', get_string('question', 'quiz'), array('cols'=>40, 'rows'=>3));
+ $repeated[] =& $mform->createElement('text', 'subanswers', get_string('answer', 'quiz'), array('size'=>50));
+
+ if (isset($this->question->options)){
+ $countsubquestions = count($this->question->options->subquestions);
+ } else {
+ $countsubquestions = 0;
+ }
+ $repeatsatstart = (QUESTION_NUMANS_START > ($countsubquestions + QUESTION_NUMANS_ADD))?
+ QUESTION_NUMANS_START : ($countsubquestions + QUESTION_NUMANS_ADD);
+ $repeatedoptions = array();
+ $repeatedoptions['subanswer']['type'] = PARAM_TEXT;
+ $repeatedoptions['subquestion']['type'] = PARAM_TEXT;
+
+ $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', QUESTION_NUMANS_ADD, get_string('addmoreqblanks', 'qtype_match'));
+
+ }
+
+ function set_defaults($question) {
+ if (isset($question->options)){
+ $subquestions = $question->options->subquestions;
+ if (count($subquestions)) {
+ $key = 0;
+ foreach ($subquestions as $subquestion){
+ $default_values['subanswers['.$key.']'] = $subquestion->answertext;
+ $default_values['subquestions['.$key.']'] = $subquestion->questiontext;
+ $key++;
+ }
+ }
+ $default_values['shuffleanswers'] = $question->options->shuffleanswers;
+ $question = (object)((array)$question + $default_values);
+ }
+ parent::set_defaults($question);
+ }
+
+ function qtype() {
+ return 'match';
+ }
+
+ function validation($data){
+ $errors = array();
+ print_object($data);
+ $answers = $data['subanswers'];
+ $questions = $data['subquestions'];
+ $questioncount = 0;
+ foreach ($questions as $key => $question){
+ $trimmedquestion = trim($question);
+ $trimmedanswer = trim($answers[$key]);
+ if (!empty($trimmedanswer) && !empty($trimmedquestion)){
+ $questioncount++;
+ }
+ if (!empty($trimmedquestion) && empty($trimmedanswer)){
+ $errors['subanswers['.$key.']'] = get_string('nomatchinganswerforq', 'qtype_match', $trimmedquestion);
+ }
+ }
+ if ($questioncount==0){
+ $errors['subquestions[0]'] = get_string('notenoughquestions', 'qtype_match', 3);
+ $errors['subquestions[1]'] = get_string('notenoughquestions', 'qtype_match', 3);
+ $errors['subquestions[2]'] = get_string('notenoughquestions', 'qtype_match', 3);
+ } elseif ($questioncount==1){
+ $errors['subquestions[1]'] = get_string('notenoughquestions', 'qtype_match', 3);
+ $errors['subquestions[2]'] = get_string('notenoughquestions', 'qtype_match', 3);
+
+ } elseif ($questioncount==2){
+ $errors['subquestions[2]'] = get_string('notenoughquestions', 'qtype_match', 3);
+ }
+ return $errors;
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Defines the editing form for the multianswer question type.
+ *
+ * @copyright © 2007 Jamie Pratt
+ * @author Jamie Pratt me@jamiep.org
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package questions
+ */
+
+/**
+ * multianswer editing form definition.
+ */
+class question_edit_multianswer_form extends question_edit_form {
+
+ // No question-type specific form fields.
+
+
+ function qtype() {
+ return 'multianswer';
+ }
+}
+?>
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Defines the editing form for the multichoice question type.
+ *
+ * @copyright © 2007 Jamie Pratt
+ * @author Jamie Pratt me@jamiep.org
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package questions
+ */
+
+/**
+ * multiple choice editing form definition.
+ */
+class question_edit_multichoice_form extends question_edit_form {
+ /**
+ * Add question-type specific form fields.
+ *
+ * @param object $mform the form being built.
+ */
+ function definition_inner(&$mform) {
+ $menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
+ $mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
+
+ $mform->addElement('selectyesno', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'));
+ $mform->setHelpButton('shuffleanswers', array('multichoiceshuffle', get_string('shuffleanswers','qtype_multichoice'), 'quiz'));
+
+/* $mform->addElement('static', 'answersinstruct', get_string('choices', 'qtype_multichoice'), get_string('fillouttwochoices', 'qtype_multichoice'));
+ $mform->closeHeaderBefore('answersinstruct');
+*/
+ $creategrades = get_grade_options();
+ $gradeoptions = $creategrades->gradeoptionsfull;
+ $repeated = array();
+ $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'));
+
+ 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);
+ $repeatedoptions = array();
+ $repeatedoptions['answer']['type'] = PARAM_TEXT;
+ $repeatedoptions['fraction']['default'] = 0;
+ $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', QUESTION_NUMANS_ADD, get_string('addmorechoiceblanks', 'qtype_multichoice'));
+
+ $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
+
+ $mform->addElement('htmleditor', 'correctfeedback', get_string('correctfeedback', 'qtype_multichoice'));
+ $mform->setType('correctfeedback', PARAM_RAW);
+
+ $mform->addElement('htmleditor', 'partiallycorrectfeedback', get_string('partiallycorrectfeedback', 'qtype_multichoice'));
+ $mform->setType('partiallycorrectfeedback', PARAM_RAW);
+
+ $mform->addElement('htmleditor', 'incorrectfeedback', get_string('incorrectfeedback', 'qtype_multichoice'));
+ $mform->setType('incorrectfeedback', PARAM_RAW);
+
+ }
+
+ function set_defaults($question) {
+ if (isset($question->options)){
+ $answers = $question->options->answers;
+ if (count($answers)) {
+ $key = 0;
+ foreach ($answers as $answer){
+ $default_values['answer['.$key.']'] = $answer->answer;
+ $default_values['fraction['.$key.']'] = $answer->fraction;
+ $default_values['feedback['.$key.']'] = $answer->feedback;
+ $key++;
+ }
+ }
+ $default_values['single'] = $question->options->single;
+ $default_values['shuffleanswers'] = $question->options->shuffleanswers;
+ $default_values['correctfeedback'] = $question->options->correctfeedback;
+ $default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback;
+ $default_values['overallincorrectfeedback'] = $question->options->overallincorrectfeedback;
+ $question = (object)((array)$question + $default_values);
+ }
+ parent::set_defaults($question);
+ }
+
+ function qtype() {
+ return 'multichoice';
+ }
+
+ function validation($data){
+ $errors = array();
+ $answers = $data['answer'];
+ $answercount = 0;
+ foreach ($answers as $answer){
+ $trimmedanswer = trim($answer);
+ if (!empty($trimmedanswer)){
+ $answercount++;
+ }
+ }
+ if ($answercount==0){
+ $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
+ $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
+ } elseif ($answercount==1){
+ $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
+
+ }
+ return $errors;
+ }
+}
+?>
\ No newline at end of file
/**
* This is the base class for Moodle question types.
- *
+ *
* There are detailed comments on each method, explaining what the method is
* for, and the circumstances under which you might need to override it.
- *
+ *
* Note: the questiontype API should NOT be considered stable yet. Very few
* question tyeps have been produced yet, so we do not yet know all the places
- * where the current API is insufficient. I would rather learn from the
+ * where the current API is insufficient. I would rather learn from the
* 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.
*
* The name returned should coincide with the name of the directory
* in which this questiontype is located
- *
+ *
* @return string the name of this question type.
*/
function name() {
}
/**
- * The name this question should appear as in the create new question
+ * The name this question should appear as in the create new question
* dropdown.
- *
+ *
* @return mixed the desired string, or false to hide this question type in the menu.
*/
function menu_name() {
$name = $this->name();
$menu_name = get_string($name, 'qtype_' . $name);
if ($menu_name[0] == '[') {
- // Legacy behavior, if the string was not in the proper qtype_name
+ // Legacy behavior, if the string was not in the proper qtype_name
// language file, look it up in the quiz one.
$menu_name = get_string($this->name(), 'quiz');
}
return $menu_name;
}
-
+
/**
* @return boolean true if this question can only be graded manually.
*/
* @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) {
+ function create_editing_form($submiturl, $question) {
global $CFG;
require_once("{$CFG->dirroot}/question/type/edit_question_form.php");
- $definition_file = $CFG->dirroot.'/question/type/'.$this->name().'/edit_'.$this->name().'_question_form.php';
+ $definition_file = $CFG->dirroot.'/question/type/'.$this->name().'/edit_'.$this->name().'_form.php';
if (!(is_readable($definition_file) && is_file($definition_file))) {
return null;
}
require_once($definition_file);
- $classname = 'edit_'.$this->name().'_question_form';
+ $classname = 'question_edit_'.$this->name().'_form';
if (!class_exists($classname)) {
return null;
}
- return new $classname($submiturl);
+ return new $classname($submiturl, $question);
}
/**
* @param object $form the form submitted by the teacher
* @param object $course the course we are in
* @return object On success, return the new question object. On failure,
- * return an object as follows. If the error object has an errors field,
+ * return an object as follows. If the error object has an errors field,
* display that as an error message. Otherwise, the editing form will be
* redisplayed with validation errors, from validation_errors field, which
* is itself an object, shown next to the form fields.
function save_question($question, $form, $course) {
// This default implementation is suitable for most
// question types.
-
+
// First, save the basic question itself
$question->name = trim($form->name);
$question->questiontext = trim($form->questiontext);
return $question;
}
-
+
/**
* Saves question-type specific options
*
/* The default implementation should work for most question types
provided the member functions it calls are overridden where required.
The layout is determined by the template question.html */
-
+
global $CFG;
$isgraded = question_state_is_graded($state->last_graded);
// If this question is being shown in the context of a quiz
// get the context so we can determine whether some extra links
- // should be shown. (Don't show these links during question preview.)
+ // should be shown. (Don't show these links during question preview.)
$cm = get_coursemodule_from_instance('quiz', $cmoptions->id);
if (!empty($cm->id)) {
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
} else {
$context = get_context_instance(CONTEXT_SYSTEM, SITEID);
}
-
+
// For editing teachers print a link to an editing popup window
$editlink = '';
if ($context && has_capability('moodle/question:manage', $context)) {
}
$grade .= $question->maxgrade;
}
-
+
$comment = stripslashes($state->manualcomment);
$commentlink = '';
-
+
if (isset($options->questioncommentlink) && $context && has_capability('mod/quiz:grade', $context)) {
$strcomment = get_string('commentorgrade', 'quiz');
$commentlink = '<div class="commentlink">'.link_to_popup_window ($options->questioncommentlink.'?attempt='.$state->attempt.'&question='.$question->id,
get_string('time'),
);
}
-
+
foreach ($states as $st) {
$st->responses[''] = $st->answer;
$this->restore_session_and_responses($question, $st);
* summarizes the student's response in the given $state. This is used for
* example in the response history table
* @return string The summary of the student response
- * @param object $question
+ * @param object $question
* @param object $state The state whose responses are to be summarized
* @param int $length The maximum length of the returned string
*/
* that it is safe to use.
* @param object $teststate The state whose responses are to be
* compared. The state will be of the same age or
- * older than $state. If possible, the method should
+ * older than $state. If possible, the method should
* only use the field $teststate->responses, however
* any field that is set up by restore_session_and_responses
* can be used.
// arrays. The ordering of the arrays does not matter.
// Question types may wish to override this (eg. to ignore trailing
// white space or to make "7.0" and "7" compare equal).
-
+
return $state->responses === $teststate->responses;
}
function grade_responses(&$question, &$state, $cmoptions) {
// The default implementation uses the test_response method to
// compare what the student entered against each of the possible
- // answers stored in the question, and uses the grade from the
+ // answers stored in the question, and uses the grade from the
// first one that matches. It also sets the marks and penalty.
// This should be good enought for most simple question types.
// Make sure we don't assign negative or too high marks.
$state->raw_grade = min(max((float) $state->raw_grade,
0.0), 1.0) * $question->maxgrade;
-
+
// Update the penalty.
$state->penalty = $question->penalty * $question->maxgrade;
if (true) {
return;
}
-
+
// no need to display replacement options if the question is new
if(empty($question->id)) {
return true;
/**
* Print the start of the question editing form, including the question category,
* questionname, questiontext, image, defaultgrade, penalty and generalfeedback fields.
- *
+ *
* Three of the fields, image, defaultgrade, penalty, are optional, and
- * can be removed from the from using the $hidefields argument.
- *
+ * can be removed from the from using the $hidefields argument.
+ *
* @param object $question The question object that the form we are printing is for.
* @param array $err Array of optional error messages to display by each field.
* Used when the form is being redisplayed after validation failed.
global $CFG;
// If you edit this function, you also need to edit random/editquestion.html.
-
+
if (!in_array('image', $hidefields)) {
make_upload_directory("$course->id"); // Just in case
$coursefiles = get_directory_list("$CFG->dataroot/$course->id", $CFG->moddata);
}
}
}
-
+
include('editquestionstart.html');
}
-
+
/**
* Print the end of the question editing form, including the submit, copy,
* and cancel button, and the standard hidden fields like the sesskey and
* the question type.
- *
+ *
* @param object $question The question object that the form we are printing is for.
* @param string $submitscript Extra attributes, for example 'onsubmit="myfunction"',
* that is added to the HTML of the submit button.
*/
function print_question_form_end($question, $submitscript = '', $hiddenfields = '') {
global $USER;
-
+
// If you edit this function, you also need to edit random/editquestion.html.
include('editquestionend.html');
}
-
+
/**
* Call format_text from weblib.php with the options appropriate to question types.
- *
+ *
* @param string $text the text to format.
* @param integer $text the type of text. Normally $question->questiontextformat.
* @param object $cmoptions the context the string is being displayed in. Only $cmoptions->course is used.
$formatoptions->para = false;
return format_text($text, $textformat, $formatoptions, $cmoptions->course);
}
-
+
/// BACKUP FUNCTIONS ////////////////////////////
/*
function restore_recode_answer($state, $restore) {
// There is nothing to decode
return $state->answer;
- }
+ }
//This function restores the question_rqp_states
function restore_state($state_id,$info,$restore) {
--- /dev/null
+<?php
+/**
+ * Defines the editing form for the shortanswer question type.
+ *
+ * @copyright © 2007 Jamie Pratt
+ * @author Jamie Pratt me@jamiep.org
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package questions
+ */
+
+/**
+ * shortanswer editing form definition.
+ */
+class question_edit_shortanswer_form extends question_edit_form {
+ /**
+ * Add question-type specific form fields.
+ *
+ * @param MoodleQuickForm $mform the form being built.
+ */
+ function definition_inner(&$mform) {
+ $menu = array(get_string('caseno', 'quiz'), get_string('caseyes', 'quiz'));
+ $mform->addElement('select', 'usecase', get_string('casesensitive', 'quiz'), $menu);
+
+ $mform->addElement('static', 'answersinstruct', get_string('correctanswers', 'quiz'), get_string('filloutoneanswer', 'quiz'));
+ $mform->closeHeaderBefore('answersinstruct');
+
+ $creategrades = get_grade_options();
+ $gradeoptions = $creategrades->gradeoptions;
+ $repeated = array();
+ $repeated[] =& $mform->createElement('header', 'answerhdr', get_string('answerno', 'qtype_shortanswer', '{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'));
+
+ 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);
+ $repeatedoptions = array();
+ $repeatedoptions['answer']['type'] = PARAM_TEXT;
+ $repeatedoptions['fraction']['default'] = 0;
+ $this->repeat_elements($repeated, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', QUESTION_NUMANS_ADD, get_string('addmoreanswerblanks', 'qtype_shortanswer'));
+
+ }
+
+ function set_defaults($question) {
+ if (isset($question->options)){
+ $answers = $question->options->answers;
+ if (count($answers)) {
+ $key = 0;
+ foreach ($answers as $answer){
+ $default_values['answer['.$key.']'] = $answer->answer;
+ $default_values['fraction['.$key.']'] = $answer->fraction;
+ $default_values['feedback['.$key.']'] = $answer->feedback;
+ $key++;
+ }
+ }
+ $default_values['usecase'] = $question->options->usecase;
+ $question = (object)((array)$question + $default_values);
+ }
+ parent::set_defaults($question);
+ }
+ function validation($data){
+ $errors = array();
+ $answers = $data['answer'];
+ $answercount = 0;
+ foreach ($answers as $answer){
+ $trimmedanswer = trim($answer);
+ if (!empty($trimmedanswer)){
+ $answercount++;
+ }
+ }
+ if ($answercount==0){
+ $errors['answer[0]'] = get_string('notenoughanswers', 'quiz', 1);
+ }
+ return $errors;
+ }
+ function qtype() {
+ return 'shortanswer';
+ }
+}
+?>
\ No newline at end of file