Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

595 lines (534 sloc) 29.787 kb
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines the editing form for the calculated simplequestion type.
*
* @package qtype
* @subpackage calculatedsimple
* @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/calculated/edit_calculated_form.php');
/**
* Editing form for the calculated simplequestion type.
*
* @copyright 2007 Jamie Pratt me@jamiep.org
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_calculatedsimple_edit_form extends qtype_calculated_edit_form {
/**
* Handle to the question type for this question.
*
* @var question_calculatedsimple_qtype
*/
public $qtypeobj;
public $wildcarddisplay;
public $questiondisplay;
public $datasetdefs;
public $reload = false;
public $maxnumber = -1;
public $regenerate = true;
public $noofitems;
public $outsidelimit = false;
public $commentanswer = array();
public $answer = array();
public $nonemptyanswer = array();
public $numbererrors = array();
public $formdata = array();
public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) {
$this->regenerate = true;
$this->question = $question;
$this->qtypeobj = question_bank::get_qtype($this->question->qtype);
// get the dataset definitions for this question
// coming here everytime even when using a NoSubmitButton
// so this will only set the values to the actual question database content
// which is not what we want so this should be removed from here
// get priority to paramdatasets
$this->reload = optional_param('reload', false, PARAM_BOOL);
if (!$this->reload) { // use database data as this is first pass
// question->id == 0 so no stored datasets
if (!empty($question->id)) {
$this->datasetdefs = $this->qtypeobj->get_dataset_definitions(
$question->id, array());
if (!empty($this->datasetdefs)) {
foreach ($this->datasetdefs as $defid => $datasetdef) {
// first get the items in case their number does not correspond to itemcount
if (isset($datasetdef->id)) {
$this->datasetdefs[$defid]->items =
$this->qtypeobj->get_database_dataset_items($datasetdef->id);
if ($this->datasetdefs[$defid]->items != '') {
$datasetdef->itemcount = count($this->datasetdefs[$defid]->items);
} else {
$datasetdef->itemcount = 0;
}
}
// Get maxnumber
if ($this->maxnumber == -1 || $datasetdef->itemcount < $this->maxnumber) {
$this->maxnumber = $datasetdef->itemcount;
}
}
}
$i = 0;
foreach ($this->question->options->answers as $answer) {
$this->answer[$i] = $answer;
$i++;
}
$this->nonemptyanswer = $this->answer;
}
$datasettoremove = false;
$newdatasetvalues = false;
$newdataset = false;
} else {
// handle reload to get values from the form-elements
// answers, datasetdefs and data_items. In any case the validation
// step will warn the user of any error in settings the values.
// Verification for the specific dataset values as the other parameters
// unints, feeedback etc are handled elsewhere
// handle request buttons :
// 'analyzequestion' (Identify the wild cards {x..} present in answers)
// 'addbutton' (create new set of datatitems)
// 'updatedatasets' is handled automatically on each reload
// The analyzequestion is done every time on reload
// to detect any new wild cards so that the current display reflects
// the mandatory (i.e. in answers) datasets
// to implement : don't do any changes if the question is used in a quiz.
// If new datadef, new properties should erase items.
// Most of the data
$datasettoremove = false;
$newdatasetvalues = false;
$newdataset = false;
$dummyform = new stdClass();
$mandatorydatasets = array();
// should not test on adding a new answer
// should test if there are already olddatasets or if the 'analyzequestion'
// submit button has been clicked
if (optional_param_array('datasetdef', false, PARAM_BOOL) ||
optional_param('analyzequestion', false, PARAM_BOOL)) {
if ($dummyform->answer = optional_param_array('answer', '', PARAM_NOTAGS)) {
// there is always at least one answer...
$fraction = optional_param_array('fraction', '', PARAM_FLOAT);
$tolerance = optional_param_array('tolerance', '', PARAM_FLOAT);
$tolerancetype = optional_param_array('tolerancetype', '', PARAM_FLOAT);
$correctanswerlength = optional_param_array('correctanswerlength', '', PARAM_INT);
$correctanswerformat = optional_param_array('correctanswerformat', '', PARAM_INT);
foreach ($dummyform->answer as $key => $answer) {
if (trim($answer) != '') { // just look for non-empty
$this->answer[$key] = new stdClass();
$this->answer[$key]->answer = $answer;
$this->answer[$key]->fraction = $fraction[$key];
$this->answer[$key]->tolerance = $tolerance[$key];
$this->answer[$key]->tolerancetype = $tolerancetype[$key];
$this->answer[$key]->correctanswerlength = $correctanswerlength[$key];
$this->answer[$key]->correctanswerformat = $correctanswerformat[$key];
$this->nonemptyanswer[]= $this->answer[$key];
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
}
}
}
$this->datasetdefs = array();
// rebuild datasetdefs from old values
if ($olddef = optional_param_array('datasetdef', '', PARAM_RAW)) {
$calcmin = optional_param_array('calcmin', '', PARAM_FLOAT);
$calclength = optional_param_array('calclength', '', PARAM_INT);
$calcmax = optional_param_array('calcmax', '', PARAM_FLOAT);
$oldoptions = optional_param_array('defoptions', '', PARAM_RAW);
$newdatasetvalues = false;
$sizeofolddef = count($olddef);
for ($key = 1; $key <= $sizeofolddef; $key++) {
$def = $olddef[$key];
$this->datasetdefs[$def]= new stdClass();
$this->datasetdefs[$def]->type = 1;
$this->datasetdefs[$def]->category = 0;
$this->datasetdefs[$def]->options = $oldoptions[$key];
$this->datasetdefs[$def]->calcmin = $calcmin[$key];
$this->datasetdefs[$def]->calcmax = $calcmax[$key];
$this->datasetdefs[$def]->calclength = $calclength[$key];
//then compare with new values
if (preg_match('~^(uniform|loguniform):([^:]*):([^:]*):([0-9]*)$~',
$this->datasetdefs[$def]->options, $regs)) {
if ($this->datasetdefs[$def]->calcmin != $regs[2]||
$this->datasetdefs[$def]->calcmax != $regs[3] ||
$this->datasetdefs[$def]->calclength != $regs[4]) {
$newdatasetvalues = true;
}
}
$this->datasetdefs[$def]->options = "uniform:" .
$this->datasetdefs[$def]->calcmin . ":" .
$this->datasetdefs[$def]->calcmax . ":" .
$this->datasetdefs[$def]->calclength;
}
}
// detect new datasets
$newdataset = false;
foreach ($mandatorydatasets as $datasetname) {
if (!isset($this->datasetdefs["1-0-$datasetname"])) {
$key = "1-0-$datasetname";
$this->datasetdefs[$key] = new stdClass();
$this->datasetdefs[$key]->type = 1;
$this->datasetdefs[$key]->category = 0;
$this->datasetdefs[$key]->name = $datasetname;
$this->datasetdefs[$key]->options = "uniform:1.0:10.0:1";
$newdataset = true;
} else {
$this->datasetdefs["1-0-$datasetname"]->name = $datasetname;
}
}
// remove obsolete datasets
$datasettoremove = false;
foreach ($this->datasetdefs as $defkey => $datasetdef) {
if (!isset($datasetdef->name)) {
$datasettoremove = true;
unset($this->datasetdefs[$defkey]);
}
}
}
} // handle reload
// create items if $newdataset and noofitems > 0 and !$newdatasetvalues
// eliminate any items if $newdatasetvalues
// eliminate any items if $datasettoremove, $newdataset, $newdatasetvalues
if ($datasettoremove ||$newdataset ||$newdatasetvalues) {
foreach ($this->datasetdefs as $defkey => $datasetdef) {
$datasetdef->itemcount = 0;
unset($datasetdef->items);
}
}
$maxnumber = -1;
if (optional_param('addbutton', false, PARAM_BOOL)) {
$maxnumber = optional_param('selectadd', '', PARAM_INT); //FIXME: sloppy coding
foreach ($this->datasetdefs as $defid => $datasetdef) {
$datasetdef->itemcount = $maxnumber;
unset($datasetdef->items);
for ($numberadded = 1; $numberadded <= $maxnumber; $numberadded++) {
$datasetitem = new stdClass();
$datasetitem->itemnumber = $numberadded;
$datasetitem->id = 0;
$datasetitem->value = $this->qtypeobj->generate_dataset_item(
$datasetdef->options);
$this->datasetdefs[$defid]->items[$numberadded] = $datasetitem;
}
}
$this->maxnumber = $maxnumber;
} else {
// Handle reload dataset items
if (optional_param_array('definition', '', PARAM_NOTAGS) &&
!($datasettoremove ||$newdataset ||$newdatasetvalues)) {
$i = 1;
$fromformdefinition = optional_param_array('definition', '', PARAM_NOTAGS);
$fromformnumber = optional_param_array('number', '', PARAM_INT);
$fromformitemid = optional_param_array('itemid', '', PARAM_INT);
ksort($fromformdefinition);
foreach ($fromformdefinition as $key => $defid) {
$addeditem = new stdClass();
$addeditem->id = $fromformitemid[$i];
$addeditem->value = $fromformnumber[$i];
$addeditem->itemnumber = ceil($i / count($this->datasetdefs));
$this->datasetdefs[$defid]->items[$addeditem->itemnumber] = $addeditem;
$this->datasetdefs[$defid]->itemcount = $i;
$i++;
}
}
if (isset($addeditem->itemnumber) && $this->maxnumber < $addeditem->itemnumber) {
$this->maxnumber = $addeditem->itemnumber;
if (!empty($this->datasetdefs)) {
foreach ($this->datasetdefs as $datasetdef) {
$datasetdef->itemcount = $this->maxnumber;
}
}
}
}
parent::__construct($submiturl, $question, $category, $contexts, $formeditable);
}
/**
* Add question-type specific form fields.
*
* @param MoodleQuickForm $mform the form being built.
*/
protected function definition_inner($mform) {
$strquestionlabel = $this->qtypeobj->comment_header($this->nonemptyanswer);
$label = get_string("sharedwildcards", "qtype_calculated");
$mform->addElement('hidden', 'synchronize', 0);
$mform->addElement('hidden', 'initialcategory', 1);
$mform->setType('initialcategory', PARAM_INT);
$mform->addElement('hidden', 'reload', 1);
$mform->setType('reload', PARAM_INT);
$addfieldsname = 'updatequestion value';
$addstring = get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname);
$this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
question_bank::fraction_options(), 1, 1);
$this->add_unit_options($mform, $this);
$this->add_unit_fields($mform, $this);
$this->add_interactive_settings();
$label = "<div class='mdl-align'></div><div class='mdl-align'>" .
get_string('wildcardrole', 'qtype_calculatedsimple') . "</div>";
$mform->addElement('html', "<div class='mdl-align'>&nbsp;</div>");
// explaining the role of datasets so other strings can be shortened
$mform->addElement('html', $label);
$mform->addElement('submit', 'analyzequestion',
get_string('findwildcards', 'qtype_calculatedsimple'));
$mform->registerNoSubmitButton('analyzequestion');
$mform->closeHeaderBefore('analyzequestion');
$this->wizarddisplay = optional_param('analyzequestion', false, PARAM_BOOL);
if ($this->maxnumber != -1) {
$this->noofitems = $this->maxnumber;
} else {
$this->noofitems = 0;
}
if (!empty($this->datasetdefs)) {//So there are some datadefs
// we put them on the page
$key = 0;
$mform->addElement('header', 'additemhdr',
get_string('wildcardparam', 'qtype_calculatedsimple'));
$idx = 1;
if (!empty($this->datasetdefs)) {// unnecessary test
$j = (($this->noofitems) * count($this->datasetdefs))+1;//
foreach ($this->datasetdefs as $defkey => $datasetdef) {
$mform->addElement('static', "na[$j]",
get_string('param', 'qtype_calculated', $datasetdef->name));
$this->qtypeobj->custom_generator_tools_part($mform, $idx, $j);
$mform->addElement('hidden', "datasetdef[$idx]");
$mform->setType("datasetdef[$idx]", PARAM_RAW);
$mform->addElement('hidden', "defoptions[$idx]");
$mform->setType("defoptions[$idx]", PARAM_RAW);
$idx++;
$mform->addElement('static', "divider[$j]", '', '<hr />');
$j++;
}
}
//this should be done before the elements are created and stored as $this->formdata;
//fill out all data sets and also the fields for the next item to add.
/*Here we do already the values error analysis so that
* we could force all wild cards values display if there is an error in values.
* as using a , in a number */
$this->numbererrors = array();
if (!empty($this->datasetdefs)) {
$j = $this->noofitems * count($this->datasetdefs);
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--) {
$data = array();
$numbererrors = array();
$comment = new stdClass();
$comment->stranswers = array();
$comment->outsidelimit = false;
$comment->answers = array();
foreach ($this->datasetdefs as $defid => $datasetdef) {
if (isset($datasetdef->items[$itemnumber])) {
$this->formdata["definition[$j]"] = $defid;
$this->formdata["itemid[$j]"] =
$datasetdef->items[$itemnumber]->id;
$data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
$this->formdata["number[$j]"] = $number =
$datasetdef->items[$itemnumber]->value;
if (! is_numeric($number)) {
$a = new stdClass();
$a->name = '{'.$datasetdef->name.'}';
$a->value = $datasetdef->items[$itemnumber]->value;
if (stristr($number, ',')) {
$this->numbererrors["number[$j]"] =
get_string('nocommaallowed', 'qtype_calculated');
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
} else {
$this->numbererrors["number[$j]"] =
get_string('notvalidnumber', 'qtype_calculated', $a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
}
} else if (stristr($number, 'x')) { // hexa will pass the test
$a = new stdClass();
$a->name = '{'.$datasetdef->name.'}';
$a->value = $datasetdef->items[$itemnumber]->value;
$this->numbererrors['number['.$j.']'] =
get_string('hexanotallowed', 'qtype_calculated', $a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
} else if (is_nan($number)) {
$a = new stdClass();
$a->name = '{'.$datasetdef->name.'}';
$a->value = $datasetdef->items[$itemnumber]->value;
$this->numbererrors["number[$j]"] =
get_string('notvalidnumber', 'qtype_calculated', $a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
}
}
$j--;
}
if ($this->noofitems != 0) {
if (empty($numbererrors)) {
if (!isset($this->question->id)) {
$this->question->id = 0;
}
$this->question->questiontext = !empty($this->question->questiontext) ?
$this->question->questiontext : '';
$comment = $this->qtypeobj->comment_on_datasetitems(
$this->qtypeobj, $this->question->id,
$this->question->questiontext, $this->nonemptyanswer,
$data, $itemnumber);
if ($comment->outsidelimit) {
$this->outsidelimit = $comment->outsidelimit;
}
$totalcomment = '';
foreach ($this->nonemptyanswer as $key => $answer) {
$totalcomment .= $comment->stranswers[$key].'<br/>';
}
$this->formdata['answercomment['.$itemnumber.']'] = $totalcomment;
}
}
}
$this->formdata['selectdelete'] = '1';
$this->formdata['selectadd'] = '1';
$j = $this->noofitems * count($this->datasetdefs)+1;
$data = array(); // data for comment_on_datasetitems later
$idx = 1;
foreach ($this->datasetdefs as $defid => $datasetdef) {
$this->formdata["datasetdef[$idx]"] = $defid;
$idx++;
}
$this->formdata = $this->qtypeobj->custom_generator_set_data(
$this->datasetdefs, $this->formdata);
}
$addoptions = Array();
$addoptions['1'] = '1';
for ($i = 10; $i <= 100; $i += 10) {
$addoptions["$i"] = "$i";
}
$showoptions = Array();
$showoptions['1'] = '1';
$showoptions['2'] = '2';
$showoptions['5'] = '5';
for ($i = 10; $i <= 100; $i += 10) {
$showoptions["$i"] = "$i";
}
$mform->closeHeaderBefore('additemhdr');
$addgrp = array();
$addgrp[] = $mform->createElement('submit', 'addbutton',
get_string('generatenewitemsset', 'qtype_calculatedsimple'));
$addgrp[] = $mform->createElement('select', "selectadd", '', $addoptions);
$addgrp[] = $mform->createElement('static', "stat", '',
get_string('newsetwildcardvalues', 'qtype_calculatedsimple'));
$mform->addGroup($addgrp, 'addgrp', '', ' ', false);
$mform->registerNoSubmitButton('addbutton');
$mform->closeHeaderBefore('addgrp');
$addgrp1 = array();
$addgrp1[] = $mform->createElement('submit', 'showbutton',
get_string('showitems', 'qtype_calculatedsimple'));
$addgrp1[] = $mform->createElement('select', "selectshow", '', $showoptions);
$addgrp1[] = $mform->createElement('static', "stat", '',
get_string('setwildcardvalues', 'qtype_calculatedsimple'));
$mform->addGroup($addgrp1, 'addgrp1', '', ' ', false);
$mform->registerNoSubmitButton('showbutton');
$mform->closeHeaderBefore('addgrp1');
$mform->addElement('static', "divideradd", '', '');
if ($this->noofitems == 0) {
$mform->addElement('static', 'warningnoitems', '', '<span class="error">' .
get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple') .
'</span>');
$mform->closeHeaderBefore('warningnoitems');
} else {
$mform->addElement('header', 'additemhdr1',
get_string('wildcardvalues', 'qtype_calculatedsimple'));
$mform->closeHeaderBefore('additemhdr1');
if (!empty($this->numbererrors) || $this->outsidelimit) {
$mform->addElement('static', "alert", '', '<span class="error">' .
get_string('useadvance', 'qtype_calculatedsimple').'</span>');
}
$mform->addElement('submit', 'updatedatasets',
get_string('updatewildcardvalues', 'qtype_calculatedsimple'));
$mform->registerNoSubmitButton('updatedatasets');
$mform->setAdvanced("updatedatasets", true);
//--------------------------------------------------------------
$j = $this->noofitems * count($this->datasetdefs);
$k = optional_param('selectshow', 1, PARAM_INT);
for ($i = $this->noofitems; $i >= 1; $i--) {
foreach ($this->datasetdefs as $defkey => $datasetdef) {
if ($k > 0 || $this->outsidelimit || !empty($this->numbererrors)) {
$mform->addElement('text', "number[$j]", get_string(
'wildcard', 'qtype_calculatedsimple', $datasetdef->name));
$mform->setAdvanced("number[$j]", true);
if (!empty($this->numbererrors['number['.$j.']'])) {
$mform->addElement('static', "numbercomment[$j]", '',
'<span class="error">' .
$this->numbererrors['number['.$j.']'] . '</span>');
$mform->setAdvanced("numbercomment[$j]", true);
}
} else {
$mform->addElement('hidden', "number[$j]", get_string(
'wildcard', 'qtype_calculatedsimple', $datasetdef->name));
}
$mform->setType("number[$j]", PARAM_FLOAT);
$mform->addElement('hidden', "itemid[$j]");
$mform->setType("itemid[$j]", PARAM_INT);
$mform->addElement('hidden', "definition[$j]");
$mform->setType("definition[$j]", PARAM_NOTAGS);
$j--;
}
if (!empty($strquestionlabel) && ($k > 0 || $this->outsidelimit ||
!empty($this->numbererrors))) {
$mform->addElement('static', "answercomment[$i]", "<b>" .
get_string('setno', 'qtype_calculatedsimple', $i) .
"</b>&nbsp;&nbsp;" . $strquestionlabel);
}
if ($k > 0 || $this->outsidelimit || !empty($this->numbererrors)) {
$mform->addElement('static', "divider1[$j]", '', '<hr />');
}
$k--;
}
}
} else {
$mform->addElement('static', 'warningnowildcards', '', '<span class="error">' .
get_string('atleastonewildcard', 'qtype_calculatedsimple') . '</span>');
$mform->closeHeaderBefore('warningnowildcards');
}
//----------------------------------------------------------------------
// Non standard name for button element needed so not using add_action_buttons
// hidden elements
$mform->addElement('hidden', 'id');
$mform->setType('id', 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);
if (!empty($this->question->id)) {
if ($this->question->formoptions->cansaveasnew) {
$mform->addElement('header', 'additemhdr',
get_string('converttocalculated', 'qtype_calculatedsimple'));
$mform->closeHeaderBefore('additemhdr');
$mform->addElement('checkbox', 'convert', '',
get_string('willconverttocalculated', 'qtype_calculatedsimple'));
$mform->setDefault('convert', 0);
}
}
}
public function data_preprocessing($question) {
$question = parent::data_preprocessing($question);
$question = $this->data_preprocessing_answers($question);
$question = $this->data_preprocessing_hints($question);
$question = $this->data_preprocessing_units($question);
$question = $this->data_preprocessing_unit_options($question);
// This is a bit ugly, but it loads all the dataset values.
$question = (object)((array)$question + $this->formdata);
return $question;
}
public function qtype() {
return 'calculatedsimple';
}
public function validation($data, $files) {
$errors = parent::validation($data, $files);
if (empty($data['definition'])) {
$errors['selectadd'] = get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple');
}
return $errors;
}
}
Jump to Line
Something went wrong with that request. Please try again.