.
/**
* Defines the editing form for the calculated question data set items.
*
* @package qtype
* @subpackage calculated
* @copyright 2007 Jamie Pratt me@jamiep.org
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/question/type/edit_question_form.php');
/**
* Calculated question data set items editing form definition.
*
* @copyright 2007 Jamie Pratt me@jamiep.org
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class question_dataset_dependent_items_form extends question_wizard_form {
/**
* 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
*/
public $question;
/**
* Reference to question type object
*
* @var question_dataset_dependent_questiontype
*/
public $qtypeobj;
/** @var stdClass the question category. */
protected $category;
/** @var context the context of the question category. */
protected $categorycontext;
public $datasetdefs;
public $maxnumber = -1;
public $regenerate;
public $noofitems;
public $outsidelimit = false;
public $commentanswers = array();
/**
* Add question-type specific form fields.
*
* @param MoodleQuickForm $mform the form being built.
*/
public function __construct($submiturl, $question, $regenerate) {
global $SESSION, $CFG, $DB;
$this->regenerate = $regenerate;
$this->question = $question;
$this->qtypeobj = question_bank::get_qtype($this->question->qtype);
// Validate the question category.
if (!$category = $DB->get_record('question_categories',
array('id' => $question->category))) {
print_error('categorydoesnotexist', 'question', $returnurl);
}
$this->category = $category;
$this->categorycontext = context::instance_by_id($category->contextid);
// Get the dataset defintions for this question.
if (empty($question->id)) {
$this->datasetdefs = $this->qtypeobj->get_dataset_definitions(
$question->id, $SESSION->calculated->definitionform->dataset);
} else {
if (empty($question->options)) {
$this->get_question_options($question);
}
$this->datasetdefs = $this->qtypeobj->get_dataset_definitions(
$question->id, array());
}
foreach ($this->datasetdefs as $datasetdef) {
// Get maxnumber.
if ($this->maxnumber == -1 || $datasetdef->itemcount < $this->maxnumber) {
$this->maxnumber = $datasetdef->itemcount;
}
}
foreach ($this->datasetdefs as $defid => $datasetdef) {
if (isset($datasetdef->id)) {
$this->datasetdefs[$defid]->items =
$this->qtypeobj->get_database_dataset_items($datasetdef->id);
}
}
parent::__construct($submiturl);
}
protected function definition() {
global $PAGE;
$labelsharedwildcard = get_string("sharedwildcard", "qtype_calculated");
$mform = $this->_form;
$mform->setDisableShortforms();
$strquestionlabel = $this->qtypeobj->comment_header($this->question);
if ($this->maxnumber != -1 ) {
$this->noofitems = $this->maxnumber;
} else {
$this->noofitems = 0;
}
$label = get_string("sharedwildcards", "qtype_calculated");
$html2 = $this->qtypeobj->print_dataset_definitions_category_shared(
$this->question, $this->datasetdefs);
$mform->addElement('static', 'listcategory', $label, $html2);
// ...----------------------------------------------------------------------.
$mform->addElement('submit', 'updatedatasets',
get_string('updatedatasetparam', 'qtype_calculated'));
$mform->registerNoSubmitButton('updatedatasets');
$mform->addElement('header', 'additemhdr',
get_string('itemtoadd', 'qtype_calculated'));
$idx = 1;
$data = array();
$j = (($this->noofitems) * count($this->datasetdefs))+1;
foreach ($this->datasetdefs as $defkey => $datasetdef) {
if ($datasetdef->category |= 0 ) {
$name = get_string('sharedwildcard', 'qtype_calculated', $datasetdef->name);
} else {
$name = get_string('wildcard', 'qtype_calculated', $datasetdef->name);
}
$mform->addElement('float', "number[{$j}]", $name);
$this->qtypeobj->custom_generator_tools_part($mform, $idx, $j);
$idx++;
$mform->addElement('hidden', "definition[{$j}]");
$mform->setType("definition[{$j}]", PARAM_RAW);
$mform->addElement('hidden', "itemid[{$j}]");
$mform->setType("itemid[{$j}]", PARAM_RAW);
$mform->addElement('static', "divider[{$j}]", '', '
');
$mform->setType("divider[{$j}]", PARAM_RAW);
$j++;
}
$mform->addElement('header', 'updateanswershdr',
get_string('answerstoleranceparam', 'qtype_calculated'));
$mform->addElement('submit', 'updateanswers',
get_string('updatetolerancesparam', 'qtype_calculated'));
$mform->setAdvanced('updateanswers', true);
$mform->registerNoSubmitButton('updateanswers');
$answers = fullclone($this->question->options->answers);
$key1 =1;
foreach ($answers as $key => $answer) {
$ans = shorten_text($answer->answer, 17, true);
if ($ans === '*') {
$mform->addElement('static',
'answercomment[' . ($this->noofitems+$key1) . ']', $ans);
$mform->addElement('hidden', 'tolerance['.$key.']', '');
$mform->setType('tolerance['.$key.']', PARAM_FLOAT); // No need to worry about localisation, as the value of this field will not be shown to users anymore.
$mform->setAdvanced('tolerance['.$key.']', true);
$mform->addElement('hidden', 'tolerancetype['.$key.']', '');
$mform->setType('tolerancetype['.$key.']', PARAM_RAW);
$mform->setAdvanced('tolerancetype['.$key.']', true);
$mform->addElement('hidden', 'correctanswerlength['.$key.']', '');
$mform->setType('correctanswerlength['.$key.']', PARAM_RAW);
$mform->setAdvanced('correctanswerlength['.$key.']', true);
$mform->addElement('hidden', 'correctanswerformat['.$key.']', '');
$mform->setType('correctanswerformat['.$key.']', PARAM_RAW);
$mform->setAdvanced('correctanswerformat['.$key.']', true);
} else if ( $ans !== '' ) {
$mform->addElement('static', 'answercomment[' . ($this->noofitems+$key1) . ']',
$ans);
$mform->addElement('float', 'tolerance['.$key.']',
get_string('tolerance', 'qtype_calculated'));
$mform->setAdvanced('tolerance['.$key.']', true);
$mform->addElement('select', 'tolerancetype['.$key.']',
get_string('tolerancetype', 'qtype_numerical'),
$this->qtypeobj->tolerance_types());
$mform->setAdvanced('tolerancetype['.$key.']', true);
$mform->addElement('select', 'correctanswerlength['.$key.']',
get_string('correctanswershows', 'qtype_calculated'), range(0, 9));
$mform->setAdvanced('correctanswerlength['.$key.']', true);
$answerlengthformats = array(
'1' => get_string('decimalformat', 'qtype_numerical'),
'2' => get_string('significantfiguresformat', 'qtype_calculated')
);
$mform->addElement('select', 'correctanswerformat['.$key.']',
get_string('correctanswershowsformat', 'qtype_calculated'),
$answerlengthformats);
$mform->setAdvanced('correctanswerformat['.$key.']', true);
$mform->addElement('static', 'dividertolerance', '', '
');
$mform->setAdvanced('dividertolerance', true);
}
$key1++;
}
$addremoveoptions = array();
$addremoveoptions['1']='1';
for ($i=10; $i<=100; $i+=10) {
$addremoveoptions["{$i}"] = "{$i}";
}
$showoptions = Array();
$showoptions['1']='1';
$showoptions['2']='2';
$showoptions['5']='5';
for ($i=10; $i<=100; $i+=10) {
$showoptions["{$i}"] = "{$i}";
}
$mform->addElement('header', 'addhdr', get_string('add', 'moodle'));
$mform->closeHeaderBefore('addhdr');
if ($this->qtypeobj->supports_dataset_item_generation()) {
$radiogrp = array();
$radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]',
null, get_string('reuseifpossible', 'qtype_calculated'), 0);
$radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]',
null, get_string('forceregenerationshared', 'qtype_calculated'), 1);
$radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]',
null, get_string('forceregenerationall', 'qtype_calculated'), 2);
$mform->addGroup($radiogrp, 'forceregenerationgrp',
get_string('nextitemtoadd', 'qtype_calculated'), "
", false);
}
$mform->addElement('submit', 'getnextbutton', get_string('getnextnow', 'qtype_calculated'));
$mform->addElement('static', "dividera", '', '
');
$addgrp = array();
$addgrp[] =& $mform->createElement('submit', 'addbutton', get_string('add', 'moodle'));
$addgrp[] =& $mform->createElement('select', "selectadd",
get_string('additem', 'qtype_calculated'), $addremoveoptions);
$addgrp[] = & $mform->createElement('static', "stat", "Items",
get_string('newsetwildcardvalues', 'qtype_calculatedsimple'));
$mform->addGroup($addgrp, 'addgrp', get_string('additem', 'qtype_calculated'), ' ', false);
$mform->addElement('static', "divideradd", '', '');
if ($this->noofitems > 0) {
$mform->addElement('header', 'deleteitemhdr', get_string('delete', 'moodle'));
$deletegrp = array();
$deletegrp[] = $mform->createElement('submit', 'deletebutton',
get_string('delete', 'moodle'));
$deletegrp[] = $mform->createElement('select', 'selectdelete',
get_string('deleteitem', 'qtype_calculated')."1", $addremoveoptions);
$deletegrp[] = $mform->createElement('static', "stat", "Items",
get_string('setwildcardvalues', 'qtype_calculatedsimple'));
$mform->addGroup($deletegrp, 'deletegrp', '', ' ', false);
} else {
$mform->addElement('static', 'warning', '', '' .
get_string('youmustaddatleastoneitem', 'qtype_calculated').'');
}
$addgrp1 = array();
$addgrp1[] = $mform->createElement('submit', 'showbutton',
get_string('showitems', 'qtype_calculated'));
$addgrp1[] = $mform->createElement('select', "selectshow", '' , $showoptions);
$addgrp1[] = $mform->createElement('static', "stat", '',
get_string('setwildcardvalues', 'qtype_calculated'));
$mform->addGroup($addgrp1, 'addgrp1', '', ' ', false);
$mform->registerNoSubmitButton('showbutton');
$mform->closeHeaderBefore('addgrp1');
// ...----------------------------------------------------------------------.
$j = $this->noofitems * count($this->datasetdefs);
$k = optional_param('selectshow', 1, PARAM_INT);
for ($i = $this->noofitems; $i >= 1; $i--) {
if ($k > 0) {
$mform->addElement('header', 'setnoheader' . $i, "" .
get_string('setno', 'qtype_calculated', $i)." ");
}
foreach ($this->datasetdefs as $defkey => $datasetdef) {
if ($k > 0) {
if ($datasetdef->category == 0 ) {
$mform->addElement('float', "number[{$j}]",
get_string('wildcard', 'qtype_calculated', $datasetdef->name));
} else {
$mform->addElement('float', "number[{$j}]", get_string(
'sharedwildcard', 'qtype_calculated', $datasetdef->name));
}
} else {
$mform->addElement('hidden', "number[{$j}]" , '');
$mform->setType("number[{$j}]", PARAM_LOCALISEDFLOAT); // Localisation handling has to be done manually.
}
$mform->addElement('hidden', "itemid[{$j}]");
$mform->setType("itemid[{$j}]", PARAM_INT);
$mform->addElement('hidden', "definition[{$j}]");
$mform->setType("definition[{$j}]", PARAM_NOTAGS);
$data[$datasetdef->name] =$datasetdef->items[$i]->value;
$j--;
}
if ('' != $strquestionlabel && ($k > 0 )) {
// ... $this->outsidelimit || !empty($this->numbererrors ).
$repeated[] = $mform->addElement('static', "answercomment[{$i}]", $strquestionlabel);
// Decode equations in question text.
$qtext = $this->qtypeobj->substitute_variables(
$this->question->questiontext, $data);
$textequations = $this->qtypeobj->find_formulas($qtext);
if ($textequations != '' && count($textequations) > 0 ) {
$mform->addElement('static', "divider1[{$j}]", '',
'Formulas {=..} in question text');
foreach ($textequations as $key => $equation) {
if ($formulaerrors = qtype_calculated_find_formula_errors($equation)) {
$str = $formulaerrors;
} else {
eval('$str = '.$equation.';');
}
$equation = shorten_text($equation, 17, true);
$mform->addElement('static', "textequation", "{={$equation}}", "=".$str);
}
}
}
$k--;
}
$mform->addElement('static', 'outsidelimit', '', '');
// Submit buttons.
if ($this->noofitems > 0) {
$buttonarray = array();
$buttonarray[] = $mform->createElement(
'submit', 'savechanges', get_string('savechanges'));
$previewlink = $PAGE->get_renderer('core_question')->question_preview_link(
$this->question->id, $this->categorycontext, true);
$buttonarray[] = $mform->createElement('static', 'previewlink', '', $previewlink);
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$mform->closeHeaderBefore('buttonar');
}
$this->add_hidden_fields();
$mform->addElement('hidden', 'category');
$mform->setType('category', PARAM_SEQUENCE);
$mform->addElement('hidden', 'wizard', 'datasetitems');
$mform->setType('wizard', PARAM_ALPHA);
}
public function set_data($question) {
$formdata = array();
$fromform = new stdClass();
if (isset($question->options)) {
$answers = $question->options->answers;
if (count($answers)) {
if (optional_param('updateanswers', false, PARAM_BOOL) ||
optional_param('updatedatasets', false, PARAM_BOOL)) {
foreach ($answers as $key => $answer) {
$fromform->tolerance[$key]= $this->_form->getElementValue(
'tolerance['.$key.']');
$answer->tolerance = $fromform->tolerance[$key];
$fromform->tolerancetype[$key]= $this->_form->getElementValue(
'tolerancetype['.$key.']');
if (is_array($fromform->tolerancetype[$key])) {
$fromform->tolerancetype[$key]= $fromform->tolerancetype[$key][0];
}
$answer->tolerancetype = $fromform->tolerancetype[$key];
$fromform->correctanswerlength[$key]= $this->_form->getElementValue(
'correctanswerlength['.$key.']');
if (is_array($fromform->correctanswerlength[$key])) {
$fromform->correctanswerlength[$key] =
$fromform->correctanswerlength[$key][0];
}
$answer->correctanswerlength = $fromform->correctanswerlength[$key];
$fromform->correctanswerformat[$key] = $this->_form->getElementValue(
'correctanswerformat['.$key.']');
if (is_array($fromform->correctanswerformat[$key])) {
$fromform->correctanswerformat[$key] =
$fromform->correctanswerformat[$key][0];
}
$answer->correctanswerformat = $fromform->correctanswerformat[$key];
}
$this->qtypeobj->save_question_calculated($question, $fromform);
} else {
foreach ($answers as $key => $answer) {
$formdata['tolerance['.$key.']'] = $answer->tolerance;
$formdata['tolerancetype['.$key.']'] = $answer->tolerancetype;
$formdata['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
$formdata['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
}
}
}
}
// Fill out all data sets and also the fields for the next item to add.
$j = $this->noofitems * count($this->datasetdefs);
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--) {
$data = array();
foreach ($this->datasetdefs as $defid => $datasetdef) {
if (isset($datasetdef->items[$itemnumber])) {
$value = $datasetdef->items[$itemnumber]->value;
if ($this->_form->getElementType("number[{$j}]") == 'hidden') {
// Some of the number elements are from the float type and some are from the hidden type.
// We need to manually handle localised floats for hidden elements.
$value = format_float($value, -1);
}
$formdata["number[{$j}]"] = $value;
$formdata["definition[{$j}]"] = $defid;
$formdata["itemid[{$j}]"] = $datasetdef->items[$itemnumber]->id;
$data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
}
$j--;
}
$comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $question->id,
$question->questiontext, $answers, $data, $itemnumber);
if ($comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit;
}
$totalcomment='';
foreach ($question->options->answers as $key => $answer) {
$totalcomment .= $comment->stranswers[$key].'
';
}
$formdata['answercomment['.$itemnumber.']'] = $totalcomment;
}
$formdata['nextpageparam[forceregeneration]'] = $this->regenerate;
$formdata['selectdelete'] = '1';
$formdata['selectadd'] = '1';
$j = $this->noofitems * count($this->datasetdefs)+1;
$data = array(); // Data for comment_on_datasetitems later.
// Dataset generation defaults.
if ($this->qtypeobj->supports_dataset_item_generation()) {
$itemnumber = $this->noofitems+1;
foreach ($this->datasetdefs as $defid => $datasetdef) {
if (!optional_param('updatedatasets', false, PARAM_BOOL) &&
!optional_param('updateanswers', false, PARAM_BOOL)) {
$value = $this->qtypeobj->generate_dataset_item($datasetdef->options);
} else {
$value = $this->_form->getElementValue("number[{$j}]");
}
if ($this->_form->getElementType("number[{$j}]") == 'hidden') {
// Some of the number elements are from the float type and some are from the hidden type.
// We need to manually handle localised floats for hidden elements.
$value = format_float($value, -1);
}
$formdata["number[{$j}]"] = $value;
$formdata["definition[{$j}]"] = $defid;
$formdata["itemid[{$j}]"] = isset($datasetdef->items[$itemnumber]) ?
$datasetdef->items[$itemnumber]->id : 0;
$data[$datasetdef->name] = $formdata["number[{$j}]"];
$j++;
}
}
// Existing records override generated data depending on radio element.
$j = $this->noofitems * count($this->datasetdefs) + 1;
if (!$this->regenerate && !optional_param('updatedatasets', false, PARAM_BOOL) &&
!optional_param('updateanswers', false, PARAM_BOOL)) {
$itemnumber = $this->noofitems + 1;
foreach ($this->datasetdefs as $defid => $datasetdef) {
if (isset($datasetdef->items[$itemnumber])) {
$formdata["number[{$j}]"] = $datasetdef->items[$itemnumber]->value;
$formdata["definition[{$j}]"] = $defid;
$formdata["itemid[{$j}]"] = $datasetdef->items[$itemnumber]->id;
$data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
}
$j++;
}
}
$comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $question->id,
$question->questiontext, $answers, $data, ($this->noofitems + 1));
if (isset($comment->outsidelimit) && $comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit;
}
$key1 = 1;
foreach ($question->options->answers as $key => $answer) {
$formdata['answercomment['.($this->noofitems+$key1).']'] = $comment->stranswers[$key];
$key1++;
}
if ($this->outsidelimit) {
$formdata['outsidelimit']= '' .
get_string('oneanswertrueansweroutsidelimits', 'qtype_calculated') . '';
}
$formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $formdata);
parent::set_data((object)($formdata + (array)$question));
}
public function validation($data, $files) {
$errors = array();
if (isset($data['savechanges']) && ($this->noofitems==0) ) {
$errors['warning'] = get_string('warning', 'mnet');
}
if ($this->outsidelimit) {
$errors['outsidelimits'] =
get_string('oneanswertrueansweroutsidelimits', 'qtype_calculated');
}
$numbers = $data['number'];
foreach ($numbers as $key => $number) {
if (! is_numeric($number)) {
if (stristr($number, ',')) {
$errors['number['.$key.']'] = get_string('nocommaallowed', 'qtype_calculated');
} else {
$errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
}
} else if (stristr($number, 'x')) {
$a = new stdClass();
$a->name = '';
$a->value = $number;
$errors['number['.$key.']'] = get_string('hexanotallowed', 'qtype_calculated', $a);
} else if (is_nan($number)) {
$errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated');
}
}
return $errors;
}
}