name = trim($form->name);
$question->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat;
$question->parent = isset($form->parent)? $form->parent : 0;
$question->length = $this->actual_number_of_questions($question);
$question->penalty = isset($form->penalty) ? $form->penalty : 0;
if (empty($form->image)) {
$question->image = "";
} else {
$question->image = $form->image;
}
if (empty($question->name)) {
$question->name = substr(strip_tags($question->questiontext), 0, 15);
if (empty($question->name)) {
$question->name = '-';
}
}
if ($question->penalty > 1 or $question->penalty < 0) {
$question->errors['penalty'] = get_string('invalidpenalty', 'quiz');
}
if (isset($form->defaultgrade)) {
$question->defaultgrade = $form->defaultgrade;
}
// Set the unique code
// TODO: set the stamp to a hash of the questiondata so that identical
// questions will get the same stamp. That will elliminate possible
// duplication during backup when questions get changed without changes
$question->stamp = make_unique_id_code();
if (!empty($question->id)) { // Question already exists
$question->version ++; // Update version number of question
if (!update_record("question", $question)) {
error("Could not update question!");
}
} else { // Question is a new one
$question->version = 1;
if (!$question->id = insert_record("question", $question)) {
error("Could not insert new question!");
}
}
// Now to save all the answers and type-specific options
$form->id = $question->id;
$form->qtype = $question->qtype;
$form->category = $question->category;
$result = $this->save_question_options($form);
if (!empty($result->error)) {
error($result->error);
}
if (!empty($result->notice)) {
notice($result->notice, "question.php?id=$question->id");
}
if (!empty($result->noticeyesno)) {
notice_yesno($result->noticeyesno, "question.php?id=$question->id", "edit.php");
print_footer($course);
exit;
}
return $question;
}
/**
* Saves question-type specific options
*
* This is called by {@link save_question()} to save the question-type specific data
* @return object $result->error or $result->noticeyesno or $result->notice
* @param object $question This holds the information from the editing form,
* it is not a standard question object.
*/
function save_question_options($question) {
return null;
}
/**
* Changes all states for the given attempts over to a new question
*
* This is used by the versioning code if the teacher requests that a question
* gets replaced by the new version. In order for the attempts to be regraded
* properly all data in the states referring to the old question need to be
* changed to refer to the new version instead. In particular for question types
* that use the answers table the answers belonging to the old question have to
* be changed to those belonging to the new version.
*
* @param integer $oldquestionid The id of the old question
* @param object $newquestion The new question
* @param array $attempts An array of all attempt objects in whose states
* replacement should take place
*/
function replace_question_in_attempts($oldquestionid, $newquestion, $attemtps) {
echo 'Not yet implemented';
return;
}
/**
* Loads the question type specific options for the question.
*
* This function loads any question type specific options for the
* question from the database into the question object. This information
* is placed in the $question->options field. A question type is
* free, however, to decide on a internal structure of the options field.
* @return bool Indicates success or failure.
* @param object $question The question object for the question. This object
* should be updated to include the question type
* specific information (it is passed by reference).
*/
function get_question_options(&$question) {
if (!isset($question->options)) {
$question->options = new object;
}
// The default implementation attaches all answers for this question
if (!$question->options->answers = get_records('question_answers', 'question',
$question->id)) {
//notify('Error: Missing question answers!');
return false;
}
return true;
}
/**
* Deletes states from the question-type specific tables
*
* @param string $stateslist Comma separated list of state ids to be deleted
*/
function delete_states($stateslist) {
/// The default question type does not have any tables of its own
// therefore there is nothing to delete
return true;
}
/**
* Deletes a question from the question-type specific tables
*
* @return boolean Success/Failure
* @param object $question The question being deleted
*/
function delete_question($questionid) {
/// The default question type does not have any tables of its own
// therefore there is nothing to delete
return true;
}
/**
* Returns the number of question numbers which are used by the question
*
* This function returns the number of question numbers to be assigned
* to the question. Most question types will have length one; they will be
* assigned one number. The 'description' type, however does not use up a
* number and so has a length of zero. Other question types may wish to
* handle a bundle of questions and hence return a number greater than one.
* @return integer The number of question numbers which should be
* assigned to the question.
* @param object $question The question whose length is to be determined.
* Question type specific information is included.
*/
function actual_number_of_questions($question) {
// By default, each question is given one number
return 1;
}
/**
* Creates empty session and response information for the question
*
* This function is called to start a question session. Empty question type
* specific session data (if any) and empty response data will be added to the
* state object. Session data is any data which must persist throughout the
* attempt possibly with updates as the user interacts with the
* question. This function does NOT create new entries in the database for
* the session; a call to the {@link save_session_and_responses} member will
* occur to do this.
* @return bool Indicates success or failure.
* @param object $question The question for which the session is to be
* created. Question type specific information is
* included.
* @param object $state The state to create the session for. Note that
* this will not have been saved in the database so
* there will be no id. This object will be updated
* to include the question type specific information
* (it is passed by reference). In particular, empty
* responses will be created in the ->responses
* field.
* @param object $cmoptions
* @param object $attempt The attempt for which the session is to be
* started. Questions may wish to initialize the
* session in different ways depending on the user id
* or time available for the attempt.
*/
function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
// The default implementation should work for the legacy question types.
// Most question types with only a single form field for the student's response
// will use the empty string '' as the index for that one response. This will
// automatically be stored in and restored from the answer field in the
// question_states table.
$state->responses = array('' => '');
return true;
}
/**
* Restores the session data and most recent responses for the given state
*
* This function loads any session data associated with the question
* session in the given state from the database into the state object.
* In particular it loads the responses that have been saved for the given
* state into the ->responses member of the state object.
*
* Question types with only a single form field for the student's response
* will not need not restore the responses; the value of the answer
* field in the question_states table is restored to ->responses['']
* before this function is called. Question types with more response fields
* should override this method and set the ->responses field to an
* associative array of responses.
* @return bool Indicates success or failure.
* @param object $question The question object for the question including any
* question type specific information.
* @param object $state The saved state to load the session for. This
* object should be updated to include the question
* type specific session information and responses
* (it is passed by reference).
*/
function restore_session_and_responses(&$question, &$state) {
// The default implementation does nothing (successfully)
return true;
}
/**
* Saves the session data and responses for the given question and state
*
* This function saves the question type specific session data from the
* state object to the database. In particular for most question types it saves the
* responses from the ->responses member of the state object. The question type
* non-specific data for the state has already been saved in the question_states
* table and the state object contains the corresponding id and
* sequence number which may be used to index a question type specific table.
*
* Question types with only a single form field for the student's response
* which is contained in ->responses[''] will not have to save this response,
* it will already have been saved to the answer field of the question_states table.
* Question types with more response fields should override this method and save
* the responses in their own database tables.
* @return bool Indicates success or failure.
* @param object $question The question object for the question including
* the question type specific information.
* @param object $state The state for which the question type specific
* data and responses should be saved.
*/
function save_session_and_responses(&$question, &$state) {
// The default implementation does nothing (successfully)
return true;
}
/**
* Returns an array of values which will give full marks if graded as
* the $state->responses field
*
* The correct answer to the question in the given state, or an example of
* a correct answer if there are many, is returned. This is used by some question
* types in the {@link grade_responses()} function but it is also used by the
* question preview screen to fill in correct responses.
* @return mixed A response array giving the responses corresponding
* to the (or a) correct answer to the question. If there is
* no correct answer that scores 100% then null is returned.
* @param object $question The question for which the correct answer is to
* be retrieved. Question type specific information is
* available.
* @param object $state The state of the question, for which a correct answer is
* needed. Question type specific information is included.
*/
function get_correct_responses(&$question, &$state) {
/* The default implementation returns the response for the first answer
that gives full marks. */
if ($question->options->answers) {
foreach ($question->options->answers as $answer) {
if (((int) $answer->fraction) === 1) {
return array('' => $answer->answer);
}
}
}
return null;
}
/**
* Return an array of values with the texts for all possible responses stored
* for the question
*
* All answers are found and their text values isolated
* @return object A mixed object
* ->id question id. Needed to manage random questions:
* it's the id of the actual question presented to user in a given attempt
* ->responses An array of values giving the responses corresponding
* to all answers to the question. Answer ids are used as keys.
* The text and partial credit are the object components
* @param object $question The question for which the answers are to
* be retrieved. Question type specific information is
* available.
*/
// ULPGC ecastro
function get_all_responses(&$question, &$state) {
unset($answers);
if (is_array($question->options->answers)) {
foreach ($question->options->answers as $aid=>$answer) {
unset ($r);
$r->answer = $answer->answer;
$r->credit = $answer->fraction;
$answers[$aid] = $r;
}
} else {
$answers[]="error"; // just for debugging, eliminate
}
$result->id = $question->id;
$result->responses = $answers;
return $result;
}
/**
* Return the actual response to the question in a given state
* for the question
*
* @return mixed An array containing the response or reponses (multiple answer, match)
* given by the user in a particular attempt.
* @param object $question The question for which the correct answer is to
* be retrieved. Question type specific information is
* available.
* @param object $state The state object that corresponds to the question,
* for which a correct answer is needed. Question
* type specific information is included.
*/
// ULPGC ecastro
function get_actual_response($question, $state) {
// change length to truncate responses here if you want
$lmax = 40;
if (!empty($state->responses)) {
$responses[] = (strlen($state->responses['']) > $lmax) ?
substr($state->responses[''], 0, $lmax).'...' : $state->responses[''];
} else {
$responses[] = '';
}
return $responses;
}
// ULPGC ecastro
function get_fractional_grade(&$question, &$state) {
$maxgrade = $question->maxgrade;
$grade = $state->grade;
if ($maxgrade) {
return (float)($grade/$maxgrade);
} else {
return (float)$grade;
}
}
/**
* Checks if the response given is correct and returns the id
*
* @return int The ide number for the stored answer that matches the response
* given by the user in a particular attempt.
* @param object $question The question for which the correct answer is to
* be retrieved. Question type specific information is
* available.
* @param object $state The state object that corresponds to the question,
* for which a correct answer is needed. Question
* type specific information is included.
*/
// ULPGC ecastro
function check_response(&$question, &$state){
return false;
}
/**
* Prints the question including the number, grading details, content,
* feedback and interactions
*
* This function prints the question including the question number,
* grading details, content for the question, any feedback for the previously
* submitted responses and the interactions. The default implementation calls
* various other methods to print each of these parts and most question types
* will just override those methods.
* @param object $question The question to be rendered. Question type
* specific information is included. The
* maximum possible grade is in ->maxgrade. The name
* prefix for any named elements is in ->name_prefix.
* @param object $state The state to render the question in. The grading
* information is in ->grade, ->raw_grade and
* ->penalty. The current responses are in
* ->responses. This is an associative array (or the
* empty string or null in the case of no responses
* submitted). The last graded state is in
* ->last_graded (hence the most recently graded
* responses are in ->last_graded->responses). The
* question type specific information is also
* included.
* @param integer $number The number for this question.
* @param object $cmoptions
* @param object $options An object describing the rendering options.
*/
function print_question(&$question, &$state, $number, $cmoptions, $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;
// For editing teachers print a link to an editing popup window
$editlink = '';
if (isteacheredit($cmoptions->course)) {
$stredit = get_string('edit');
$linktext = '
';
$editlink = link_to_popup_window('/question/question.php?id='.$question->id, $stredit, $linktext, 450, 550, $stredit, '', true);
}
$grade = '';
if ($question->maxgrade and $options->scores) {
if ($cmoptions->optionflags & QUESTION_ADAPTIVE) {
$grade = (!question_state_is_graded($state->last_graded)) ? '--/' : round($state->last_graded->grade, $cmoptions->decimalpoints).'/';
}
$grade .= $question->maxgrade;
}
$history = $this->history($question, $state, $number, $cmoptions, $options);
include "$CFG->dirroot/question/type/question.html";
}
/*
* Print history of responses
*
* Used by print_question()
*/
function history($question, $state, $number, $cmoptions, $options) {
$history = '';
if(isset($options->history) and $options->history) {
if ($options->history == 'all') {
// show all states
$states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event > '0'", 'seq_number DESC');
} else {
// show only graded states
$states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event IN (".QUESTION_EVENTGRADE.','.QUESTION_EVENTCLOSEANDGRADE.")", 'seq_number DESC');
}
if (count($states) > 1) {
$strreviewquestion = get_string('reviewresponse', 'quiz');
unset($table);
$table->head = array (
get_string('numberabbr', 'quiz'),
get_string('action', 'quiz'),
get_string('response', 'quiz'),
get_string('time'),
get_string('score', 'quiz'),
get_string('penalty', 'quiz'),
get_string('grade', 'quiz'),
);
$table->align = array ('center', 'center', 'left', 'left', 'left', 'left', 'left');
$table->size = array ('', '', '', '', '', '', '');
$table->width = '100%';
foreach ($states as $st) {
$st->responses[''] = $st->answer;
$this->restore_session_and_responses($question, $st);
$b = ($state->id == $st->id) ? '' : '';
$be = ($state->id == $st->id) ? '' : '';
if ($state->id == $st->id) {
$link = ''.$st->seq_number.'';
} else {
if(isset($options->questionreviewlink)) {
$link = link_to_popup_window ($options->questionreviewlink.'?state='.$st->id.'&number='.$number,
'reviewquestion', $st->seq_number, 450, 650, $strreviewquestion, 'none', true);
} else {
$link = $st->seq_number;
}
}
$table->data[] = array (
$link,
$b.get_string('event'.$st->event, 'quiz').$be,
$b.$this->response_summary($question, $st).$be,
$b.userdate($st->timestamp, get_string('timestr', 'quiz')).$be,
$b.round($st->raw_grade, $cmoptions->decimalpoints).$be,
$b.round($st->penalty, $cmoptions->decimalpoints).$be,
$b.round($st->grade, $cmoptions->decimalpoints).$be
);
}
$history = make_table($table);
}
}
return $history;
}
/**
* Prints the score obtained and maximum score available plus any penalty
* information
*
* This function prints a summary of the scoring in the most recently
* graded state (the question may not have been submitted for marking at
* the current state). The default implementation should be suitable for most
* question types.
* @param object $question The question for which the grading details are
* to be rendered. Question type specific information
* is included. The maximum possible grade is in
* ->maxgrade.
* @param object $state The state. In particular the grading information
* is in ->grade, ->raw_grade and ->penalty.
* @param object $cmoptions
* @param object $options An object describing the rendering options.
*/
function print_question_grading_details(&$question, &$state, $cmoptions, $options) {
/* The default implementation prints the number of marks if no attempt
has been made. Otherwise it displays the grade obtained out of the
maximum grade available and a warning if a penalty was applied for the
attempt and displays the overall grade obtained counting all previous
responses (and penalties) */
if (QUESTION_EVENTDUPLICATE == $state->event) {
echo ' ';
print_string('duplicateresponse', 'quiz');
}
if (!empty($question->maxgrade) && $options->scores) {
if (question_state_is_graded($state->last_graded)) {
// Display the grading details from the last graded state
$grade->cur = round($state->last_graded->grade, $cmoptions->decimalpoints);
$grade->max = $question->maxgrade;
$grade->raw = round($state->last_graded->raw_grade, $cmoptions->decimalpoints);
// let student know wether the answer was correct
echo '
| $strquizname | \n"; echo "$strdoreplace | \n"; echo "$straffectedstudents | \n"; echo "
|---|---|---|
| ".format_string($quiz->name)." | \n"; echo "id}replace\" type=\"checkbox\" ".$checked." /> | \n"; echo "".(($studentcount) ? $studentcount.' '.$strstudents : '-')." | \n"; echo "