diff --git a/question/engine/tests/helpers.php b/question/engine/tests/helpers.php
index a02cd0c7315..69c02d08f33 100644
--- a/question/engine/tests/helpers.php
+++ b/question/engine/tests/helpers.php
@@ -404,8 +404,12 @@ class test_question_maker {
$essay->qtype = question_bank::get_qtype('essay');
$essay->responseformat = 'editor';
+ $essay->responserequired = 1;
$essay->responsefieldlines = 15;
$essay->attachments = 0;
+ $essay->attachmentsrequired = 0;
+ $essay->responsetemplate = '';
+ $essay->responsetemplateformat = FORMAT_MOODLE;
$essay->graderinfo = '';
$essay->graderinfoformat = FORMAT_MOODLE;
diff --git a/question/format/blackboard_six/formatpool.php b/question/format/blackboard_six/formatpool.php
index 8073055bdf8..07b4ec39608 100644
--- a/question/format/blackboard_six/formatpool.php
+++ b/question/format/blackboard_six/formatpool.php
@@ -153,8 +153,10 @@ class qformat_blackboard_six_pool extends qformat_blackboard_six_base {
$question->responsetemplate = $this->text_field('');
$question->feedback = '';
$question->responseformat = 'editor';
+ $question->responserequired = 1;
$question->responsefieldlines = 15;
$question->attachments = 0;
+ $question->attachmentsrequired = 0;
$question->fraction = 0;
$questions[] = $question;
diff --git a/question/format/blackboard_six/formatqti.php b/question/format/blackboard_six/formatqti.php
index 8349cb08538..bb8777c3180 100644
--- a/question/format/blackboard_six/formatqti.php
+++ b/question/format/blackboard_six/formatqti.php
@@ -778,8 +778,10 @@ class qformat_blackboard_six_qti extends qformat_blackboard_six_base {
$question->fraction[] = 1;
$question->defaultmark = 1;
$question->responseformat = 'editor';
+ $question->responserequired = 1;
$question->responsefieldlines = 15;
$question->attachments = 0;
+ $question->attachmentsrequired = 0;
$question->responsetemplate = $this->text_field('');
$questions[]=$question;
diff --git a/question/format/examview/format.php b/question/format/examview/format.php
index 36ad9c7e130..e2e1a9af611 100644
--- a/question/format/examview/format.php
+++ b/question/format/examview/format.php
@@ -293,10 +293,12 @@ class qformat_examview extends qformat_based_on_xml {
$feedback = trim($this->unxmlise($qrec['answer'][0]['#']));
$question->graderinfo = $this->text_field($feedback);
$question->responsetemplate = $this->text_field('');
+ $question->responserequired = 1;
$question->feedback = $feedback;
$question->responseformat = 'editor';
$question->responsefieldlines = 15;
$question->attachments = 0;
+ $question->attachmentsrequired = 0;
$question->fraction = 0;
return $question;
}
diff --git a/question/format/gift/format.php b/question/format/gift/format.php
index 9089ab10ca0..156d4a888a0 100644
--- a/question/format/gift/format.php
+++ b/question/format/gift/format.php
@@ -327,8 +327,10 @@ class qformat_gift extends qformat_default {
case 'essay':
$question->responseformat = 'editor';
+ $question->responserequired = 1;
$question->responsefieldlines = 15;
$question->attachments = 0;
+ $question->attachmentsrequired = 0;
$question->graderinfo = array(
'text' => '', 'format' => FORMAT_HTML, 'files' => array());
$question->responsetemplate = array(
diff --git a/question/format/webct/format.php b/question/format/webct/format.php
index 9bc4f3a4672..5c45f1dbc65 100644
--- a/question/format/webct/format.php
+++ b/question/format/webct/format.php
@@ -634,8 +634,10 @@ class qformat_webct extends qformat_default {
$question = $this->defaultquestion();
$question->qtype = 'essay';
$question->responseformat = 'editor';
+ $question->responserequired = 1;
$question->responsefieldlines = 15;
$question->attachments = 0;
+ $question->attachmentsrequired = 0;
$question->graderinfo = array(
'text' => '',
'format' => FORMAT_HTML,
diff --git a/question/format/xml/format.php b/question/format/xml/format.php
index 1c2edafdd2e..43c17e03df6 100644
--- a/question/format/xml/format.php
+++ b/question/format/xml/format.php
@@ -726,8 +726,12 @@ class qformat_xml extends qformat_default {
array('#', 'responseformat', 0, '#'), 'editor');
$qo->responsefieldlines = $this->getpath($question,
array('#', 'responsefieldlines', 0, '#'), 15);
+ $qo->responserequired = $this->getpath($question,
+ array('#', 'responserequired', 0, '#'), 1);
$qo->attachments = $this->getpath($question,
array('#', 'attachments', 0, '#'), 0);
+ $qo->attachmentsrequired = $this->getpath($question,
+ array('#', 'attachmentsrequired', 0, '#'), 0);
$qo->graderinfo = $this->import_text_with_files($question,
array('#', 'graderinfo', 0), '', $this->get_format($qo->questiontextformat));
$qo->responsetemplate['text'] = $this->getpath($question,
@@ -1273,10 +1277,14 @@ class qformat_xml extends qformat_default {
case 'essay':
$expout .= " " . $question->options->responseformat .
"\n";
+ $expout .= " " . $question->options->responserequired .
+ "\n";
$expout .= " " . $question->options->responsefieldlines .
"\n";
$expout .= " " . $question->options->attachments .
"\n";
+ $expout .= " " . $question->options->attachmentsrequired .
+ "\n";
$expout .= " format($question->options->graderinfoformat) . ">\n";
$expout .= $this->writetext($question->options->graderinfo, 3);
diff --git a/question/format/xml/tests/xmlformat_test.php b/question/format/xml/tests/xmlformat_test.php
index 72d8fb80625..448cc279370 100644
--- a/question/format/xml/tests/xmlformat_test.php
+++ b/question/format/xml/tests/xmlformat_test.php
@@ -355,8 +355,10 @@ END;
$expectedq->length = 1;
$expectedq->penalty = 0;
$expectedq->responseformat = 'editor';
+ $expectedq->responserequired = 1;
$expectedq->responsefieldlines = 15;
$expectedq->attachments = 0;
+ $expectedq->attachmentsrequired = 0;
$expectedq->graderinfo['text'] = '';
$expectedq->graderinfo['format'] = FORMAT_MOODLE;
$expectedq->responsetemplate['text'] = '';
@@ -380,8 +382,10 @@ END;
0
0
monospaced
+ 0
42
-1
+ 1
Grade generously!
]]>
@@ -404,8 +408,10 @@ END;
$expectedq->length = 1;
$expectedq->penalty = 0;
$expectedq->responseformat = 'monospaced';
+ $expectedq->responserequired = 0;
$expectedq->responsefieldlines = 42;
$expectedq->attachments = -1;
+ $expectedq->attachmentsrequired = 1;
$expectedq->graderinfo['text'] = 'Grade generously!
';
$expectedq->graderinfo['format'] = FORMAT_HTML;
$expectedq->responsetemplate['text'] = 'Here is something really interesting.
';
@@ -432,8 +438,10 @@ END;
$qdata->options->id = 456;
$qdata->options->questionid = 123;
$qdata->options->responseformat = 'monospaced';
+ $qdata->options->responserequired = 0;
$qdata->options->responsefieldlines = 42;
$qdata->options->attachments = -1;
+ $qdata->options->attachmentsrequired = 1;
$qdata->options->graderinfo = 'Grade generously!
';
$qdata->options->graderinfoformat = FORMAT_HTML;
$qdata->options->responsetemplate = 'Here is something really interesting.
';
@@ -456,8 +464,10 @@ END;
0
0
monospaced
+ 0
42
-1
+ 1
Grade generously!]]>
diff --git a/question/type/essay/backup/moodle1/lib.php b/question/type/essay/backup/moodle1/lib.php
index 51b17984bdf..fcb0aec6cd7 100644
--- a/question/type/essay/backup/moodle1/lib.php
+++ b/question/type/essay/backup/moodle1/lib.php
@@ -43,8 +43,10 @@ class moodle1_qtype_essay_handler extends moodle1_qtype_handler {
$this->write_xml('essay', array(
'id' => $this->converter->get_nextid(),
'responseformat' => 'editor',
+ 'responserequired' => 1,
'responsefieldlines' => 15,
'attachments' => 0,
+ 'attachmentsrequired' => 0,
'graderinfo' => '',
'graderinfoformat' => FORMAT_HTML,
'responsetemplate' => '',
diff --git a/question/type/essay/backup/moodle2/backup_qtype_essay_plugin.class.php b/question/type/essay/backup/moodle2/backup_qtype_essay_plugin.class.php
index b53753cb0ec..fb4e5f792f6 100644
--- a/question/type/essay/backup/moodle2/backup_qtype_essay_plugin.class.php
+++ b/question/type/essay/backup/moodle2/backup_qtype_essay_plugin.class.php
@@ -49,9 +49,9 @@ class backup_qtype_essay_plugin extends backup_qtype_plugin {
// Now create the qtype own structures.
$essay = new backup_nested_element('essay', array('id'), array(
- 'responseformat', 'responsefieldlines', 'attachments',
- 'graderinfo', 'graderinfoformat', 'responsetemplate',
- 'responsetemplateformat'));
+ 'responseformat', 'responserequired', 'responsefieldlines',
+ 'attachments', 'attachmentsrequired', 'graderinfo',
+ 'graderinfoformat', 'responsetemplate', 'responsetemplateformat'));
// Now the own qtype tree.
$pluginwrapper->add_child($essay);
diff --git a/question/type/essay/backup/moodle2/restore_qtype_essay_plugin.class.php b/question/type/essay/backup/moodle2/restore_qtype_essay_plugin.class.php
index 7a56430c40d..36b929c2fdf 100644
--- a/question/type/essay/backup/moodle2/restore_qtype_essay_plugin.class.php
+++ b/question/type/essay/backup/moodle2/restore_qtype_essay_plugin.class.php
@@ -58,6 +58,12 @@ class restore_qtype_essay_plugin extends restore_qtype_plugin {
if (!isset($data->responsetemplateformat)) {
$data->responsetemplateformat = FORMAT_HTML;
}
+ if (!isset($data->responserequired)) {
+ $data->responserequired = 1;
+ }
+ if (!isset($data->attachmentsrequired)) {
+ $data->attachmentsrequired = 0;
+ }
// Detect if the question is created or mapped.
$questioncreated = $this->get_mappingid('question_created',
@@ -103,8 +109,10 @@ class restore_qtype_essay_plugin extends restore_qtype_plugin {
$defaultoptions = new stdClass();
$defaultoptions->questionid = $q->id;
$defaultoptions->responseformat = 'editor';
+ $defaultoptions->responserequired = 1;
$defaultoptions->responsefieldlines = 15;
$defaultoptions->attachments = 0;
+ $defaultoptions->attachmentsrequired = 0;
$defaultoptions->graderinfo = '';
$defaultoptions->graderinfoformat = FORMAT_HTML;
$defaultoptions->responsetemplate = '';
diff --git a/question/type/essay/db/install.xml b/question/type/essay/db/install.xml
index c311a8f8711..7d6d94afc5f 100644
--- a/question/type/essay/db/install.xml
+++ b/question/type/essay/db/install.xml
@@ -1,5 +1,5 @@
-
@@ -9,8 +9,10 @@
+
+
@@ -22,4 +24,4 @@
-
\ No newline at end of file
+
diff --git a/question/type/essay/db/upgrade.php b/question/type/essay/db/upgrade.php
index 7d4992161dc..393eb9f54a1 100644
--- a/question/type/essay/db/upgrade.php
+++ b/question/type/essay/db/upgrade.php
@@ -140,10 +140,36 @@ function xmldb_qtype_essay_upgrade($oldversion) {
// Moodle v2.5.0 release upgrade line.
// Put any upgrade step following this.
-
// Moodle v2.6.0 release upgrade line.
// Put any upgrade step following this.
+ if ($oldversion < 2014011300) {
+
+ // Create new field responserequired (indicates whether inline response is required).
+
+ $table = new xmldb_table('qtype_essay_options');
+ $field = new xmldb_field('responserequired', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1', 'responseformat');
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Essay savepoint reached.
+ upgrade_plugin_savepoint(true, 2014011300, 'qtype', 'essay');
+ }
+
+ if ($oldversion < 2014011301) {
+
+ // Create new field attachmentsrequired (indicates whether attachments should be required).
+
+ $table = new xmldb_table('qtype_essay_options');
+ $field = new xmldb_field('attachmentsrequired', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'attachments');
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Essay savepoint reached.
+ upgrade_plugin_savepoint(true, 2014011301, 'qtype', 'essay');
+ }
return true;
}
diff --git a/question/type/essay/edit_essay_form.php b/question/type/essay/edit_essay_form.php
index edccbe5f88b..d50437b2780 100644
--- a/question/type/essay/edit_essay_form.php
+++ b/question/type/essay/edit_essay_form.php
@@ -38,22 +38,40 @@ class qtype_essay_edit_form extends question_edit_form {
protected function definition_inner($mform) {
$qtype = question_bank::get_qtype('essay');
+ $mform->addElement('header', 'responseoptions', get_string('responseoptions', 'qtype_essay'));
+ $mform->setExpanded('responseoptions');
+
$mform->addElement('select', 'responseformat',
get_string('responseformat', 'qtype_essay'), $qtype->response_formats());
$mform->setDefault('responseformat', 'editor');
+ $mform->addElement('select', 'responserequired',
+ get_string('responserequired', 'qtype_essay'), $qtype->response_required_options());
+ $mform->setDefault('responserequired', 1);
+ $mform->disabledIf('responserequired', 'responseformat', 'eq', 'noinline');
+
$mform->addElement('select', 'responsefieldlines',
get_string('responsefieldlines', 'qtype_essay'), $qtype->response_sizes());
$mform->setDefault('responsefieldlines', 15);
+ $mform->disabledIf('responsefieldlines', 'responseformat', 'eq', 'noinline');
$mform->addElement('select', 'attachments',
get_string('allowattachments', 'qtype_essay'), $qtype->attachment_options());
$mform->setDefault('attachments', 0);
+ $mform->addElement('select', 'attachmentsrequired',
+ get_string('attachmentsrequired', 'qtype_essay'), $qtype->attachments_required_options());
+ $mform->setDefault('attachmentsrequired', 0);
+ $mform->addHelpButton('attachmentsrequired', 'attachmentsrequired', 'qtype_essay');
+ $mform->disabledIf('attachmentsrequired', 'attachments', 'eq', 0);
+
+ $mform->addElement('header', 'responsetemplateheader', get_string('responsetemplateheader', 'qtype_essay'));
$mform->addElement('editor', 'responsetemplate', get_string('responsetemplate', 'qtype_essay'),
array('rows' => 10), array_merge($this->editoroptions, array('maxfiles' => 0)));
$mform->addHelpButton('responsetemplate', 'responsetemplate', 'qtype_essay');
+ $mform->addElement('header', 'graderinfoheader', get_string('graderinfoheader', 'qtype_essay'));
+ $mform->setExpanded('graderinfoheader');
$mform->addElement('editor', 'graderinfo', get_string('graderinfo', 'qtype_essay'),
array('rows' => 10), $this->editoroptions);
}
@@ -66,8 +84,10 @@ class qtype_essay_edit_form extends question_edit_form {
}
$question->responseformat = $question->options->responseformat;
+ $question->responserequired = $question->options->responserequired;
$question->responsefieldlines = $question->options->responsefieldlines;
$question->attachments = $question->options->attachments;
+ $question->attachmentsrequired = $question->options->attachmentsrequired;
$draftid = file_get_submitted_draft_itemid('graderinfo');
$question->graderinfo = array();
@@ -91,6 +111,30 @@ class qtype_essay_edit_form extends question_edit_form {
return $question;
}
+ public function validation($fromform, $files) {
+ $errors = parent::validation($fromform, $files);
+
+ // Don't allow both 'no inline response' and 'no attachments' to be selected,
+ // as these options would result in there being no input requested from the user.
+ if ($fromform['responseformat'] == 'noinline' && !$fromform['attachments']) {
+ $errors['attachments'] = get_string('mustattach', 'qtype_essay');
+ }
+
+ // If 'no inline response' is set, force the teacher to require attachments;
+ // otherwise there will be nothing to grade.
+ if ($fromform['responseformat'] == 'noinline' && !$fromform['attachmentsrequired']) {
+ $errors['attachmentsrequired'] = get_string('mustrequire', 'qtype_essay');
+ }
+
+ // Don't allow the teacher to require more attachments than they allow; as this would
+ // create a condition that it's impossible for the student to meet.
+ if ($fromform['attachments'] != -1 && $fromform['attachments'] < $fromform['attachmentsrequired'] ) {
+ $errors['attachmentsrequired'] = get_string('mustrequirefewer', 'qtype_essay');
+ }
+
+ return $errors;
+ }
+
public function qtype() {
return 'essay';
}
diff --git a/question/type/essay/lang/en/qtype_essay.php b/question/type/essay/lang/en/qtype_essay.php
index 068c4d1e76e..134fc4ba9c1 100644
--- a/question/type/essay/lang/en/qtype_essay.php
+++ b/question/type/essay/lang/en/qtype_essay.php
@@ -24,11 +24,19 @@
*/
$string['allowattachments'] = 'Allow attachments';
+$string['attachmentsoptional'] = 'Attachments are optional';
+$string['attachmentsrequired'] = 'Require attachments';
+$string['attachmentsrequired_help'] = 'This option specifies the minimum number of attachments required for a response to be considered gradable.';
$string['formateditor'] = 'HTML editor';
$string['formateditorfilepicker'] = 'HTML editor with file picker';
$string['formatmonospaced'] = 'Plain text, monospaced font';
+$string['formatnoinline'] = 'No inline text';
$string['formatplain'] = 'Plain text';
$string['graderinfo'] = 'Information for graders';
+$string['graderinfoheader'] = 'Grader Information';
+$string['mustattach'] = 'When "no inline text" is selected, or responses are optional, you must allow at least one attachment.';
+$string['mustrequire'] = 'When "no inline text" is selected, or responses are optional, you must require at least one attachment.';
+$string['mustrequirefewer'] = 'You cannot require more attachments than you allow.';
$string['nlines'] = '{$a} lines';
$string['pluginname'] = 'Essay';
$string['pluginname_help'] = 'In response to a question (that may include an image) the respondent writes an answer of a paragraph or two. The essay question will not be assigned a grade until it has been reviewed by a teacher and manually graded.';
@@ -38,5 +46,10 @@ $string['pluginnameediting'] = 'Editing an Essay question';
$string['pluginnamesummary'] = 'Allows a response of a few sentences or paragraphs. This must then be graded manually.';
$string['responsefieldlines'] = 'Input box size';
$string['responseformat'] = 'Response format';
+$string['responseoptions'] = 'Response Options';
+$string['responserequired'] = 'Require text';
+$string['responsenotrequired'] = 'Text input is optional';
+$string['responseisrequired'] = 'Require the student to enter text';
$string['responsetemplate'] = 'Response template';
+$string['responsetemplateheader'] = 'Response Template';
$string['responsetemplate_help'] = 'Any text entered here will be displayed in the response input box when a new attempt at the question starts.';
diff --git a/question/type/essay/question.php b/question/type/essay/question.php
index 582a71b6206..54f285f8200 100644
--- a/question/type/essay/question.php
+++ b/question/type/essay/question.php
@@ -34,9 +34,18 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_essay_question extends question_with_responses {
+
public $responseformat;
+
+ /** @var int Indicates whether an inline response is required ('0') or optional ('1') */
+ public $responserequired;
+
public $responsefieldlines;
public $attachments;
+
+ /** @var int The number of attachments required for a response to be complete. */
+ public $attachmentsrequired;
+
public $graderinfo;
public $graderinfoformat;
public $responsetemplate;
@@ -81,7 +90,27 @@ class qtype_essay_question extends question_with_responses {
}
public function is_complete_response(array $response) {
- return array_key_exists('answer', $response) && ($response['answer'] !== '');
+ // Determine if the given response has inline text and attachments.
+ $hasinlinetext = array_key_exists('answer', $response) && ($response['answer'] !== '');
+ $hasattachments = array_key_exists('attachments', $response)
+ && $response['attachments'] instanceof question_response_files;
+
+ // Determine the number of attachments present.
+ if ($hasattachments) {
+ $attachcount = count($response['attachments']->get_files());
+ } else {
+ $attachcount = 0;
+ }
+
+ // Determine if we have /some/ content to be graded.
+ $hascontent = $hasinlinetext || ($attachcount > 0);
+
+ // Determine if we meet the optional requirements.
+ $meetsinlinereq = $hasinlinetext || (!$this->responserequired) || ($this->responseformat == 'noinline');
+ $meetsattachmentreq = ($attachcount >= $this->attachmentsrequired);
+
+ // The response is complete iff all of our requirements are met.
+ return $hascontent && $meetsinlinereq && $meetsattachmentreq;
}
public function is_same_response(array $prevresponse, array $newresponse) {
diff --git a/question/type/essay/questiontype.php b/question/type/essay/questiontype.php
index 7ab2df6df53..8e5c580acda 100644
--- a/question/type/essay/questiontype.php
+++ b/question/type/essay/questiontype.php
@@ -63,8 +63,10 @@ class qtype_essay extends question_type {
}
$options->responseformat = $formdata->responseformat;
+ $options->responserequired = $formdata->responserequired;
$options->responsefieldlines = $formdata->responsefieldlines;
$options->attachments = $formdata->attachments;
+ $options->attachmentsrequired = $formdata->attachmentsrequired;
$options->graderinfo = $this->import_or_save_files($formdata->graderinfo,
$context, 'qtype_essay', 'graderinfo', $formdata->id);
$options->graderinfoformat = $formdata->graderinfo['format'];
@@ -76,8 +78,10 @@ class qtype_essay extends question_type {
protected function initialise_question_instance(question_definition $question, $questiondata) {
parent::initialise_question_instance($question, $questiondata);
$question->responseformat = $questiondata->options->responseformat;
+ $question->responserequired = $questiondata->options->responserequired;
$question->responsefieldlines = $questiondata->options->responsefieldlines;
$question->attachments = $questiondata->options->attachments;
+ $question->attachmentsrequired = $questiondata->options->attachmentsrequired;
$question->graderinfo = $questiondata->options->graderinfo;
$question->graderinfoformat = $questiondata->options->graderinfoformat;
$question->responsetemplate = $questiondata->options->responsetemplate;
@@ -101,6 +105,17 @@ class qtype_essay extends question_type {
'editorfilepicker' => get_string('formateditorfilepicker', 'qtype_essay'),
'plain' => get_string('formatplain', 'qtype_essay'),
'monospaced' => get_string('formatmonospaced', 'qtype_essay'),
+ 'noinline' => get_string('formatnoinline', 'qtype_essay'),
+ );
+ }
+
+ /**
+ * @return array the choices that should be offerd when asking if a response is required
+ */
+ public function response_required_options() {
+ return array(
+ 1 => get_string('responseisrequired', 'qtype_essay'),
+ 0 => get_string('responsenotrequired', 'qtype_essay'),
);
}
@@ -128,6 +143,18 @@ class qtype_essay extends question_type {
);
}
+ /**
+ * @return array the choices that should be offered for the number of required attachments.
+ */
+ public function attachments_required_options() {
+ return array(
+ 0 => get_string('attachmentsoptional', 'qtype_essay'),
+ 1 => '1',
+ 2 => '2',
+ 3 => '3'
+ );
+ }
+
public function move_files($questionid, $oldcontextid, $newcontextid) {
parent::move_files($questionid, $oldcontextid, $newcontextid);
$fs = get_file_storage();
diff --git a/question/type/essay/renderer.php b/question/type/essay/renderer.php
index ae7eafc3b3e..c977014d128 100644
--- a/question/type/essay/renderer.php
+++ b/question/type/essay/renderer.php
@@ -178,6 +178,28 @@ abstract class qtype_essay_format_renderer_base extends plugin_renderer_base {
protected abstract function class_name();
}
+/**
+ * An essay format renderer for essays where the student should not enter
+ * any inline response.
+ *
+ * @copyright 2013 Binghamton University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_essay_format_noinline_renderer extends plugin_renderer_base {
+
+ protected function class_name() {
+ return 'qtype_essay_noinline';
+ }
+
+ public function response_area_read_only($name, $qa, $step, $lines, $context) {
+ return '';
+ }
+
+ public function response_area_input($name, $qa, $step, $lines, $context) {
+ return '';
+ }
+
+}
/**
* An essay format renderer for essays where the student should use the HTML
diff --git a/question/type/essay/tests/helper.php b/question/type/essay/tests/helper.php
index 751473cff1f..0ce0ba44282 100644
--- a/question/type/essay/tests/helper.php
+++ b/question/type/essay/tests/helper.php
@@ -34,7 +34,7 @@ defined('MOODLE_INTERNAL') || die();
*/
class qtype_essay_test_helper extends question_test_helper {
public function get_test_questions() {
- return array('editor', 'editorfilepicker', 'plain', 'monospaced', 'responsetemplate');
+ return array('editor', 'editorfilepicker', 'plain', 'monospaced', 'responsetemplate', 'noinline');
}
/**
@@ -49,8 +49,10 @@ class qtype_essay_test_helper extends question_test_helper {
$q->questiontext = 'Please write a story about a frog.';
$q->generalfeedback = 'I hope your story had a beginning, a middle and an end.';
$q->responseformat = 'editor';
+ $q->responserequired = 1;
$q->responsefieldlines = 10;
$q->attachments = 0;
+ $q->attachmentsrequired = 0;
$q->graderinfo = '';
$q->graderinfoformat = FORMAT_HTML;
$q->qtype = question_bank::get_qtype('essay');
@@ -93,8 +95,10 @@ class qtype_essay_test_helper extends question_test_helper {
$fromform->defaultmark = 1.0;
$fromform->generalfeedback = array('text' => 'I hope your story had a beginning, a middle and an end.', 'format' => FORMAT_HTML);
$fromform->responseformat = 'editorfilepicker';
+ $fromform->responserequired = 1;
$fromform->responsefieldlines = 10;
$fromform->attachments = 3;
+ $fromform->attachmentsrequired = 0;
$fromform->graderinfo = array('text' => '', 'format' => FORMAT_HTML);
$fromform->responsetemplate = array('text' => '', 'format' => FORMAT_HTML);
@@ -126,8 +130,10 @@ class qtype_essay_test_helper extends question_test_helper {
$fromform->defaultmark = 1.0;
$fromform->generalfeedback = array('text' => 'I hope your story had a beginning, a middle and an end.', 'format' => FORMAT_HTML);
$fromform->responseformat = 'plain';
+ $fromform->responserequired = 1;
$fromform->responsefieldlines = 10;
$fromform->attachments = 0;
+ $fromform->attachmentsrequired = 0;
$fromform->graderinfo = array('text' => '', 'format' => FORMAT_HTML);
$fromform->responsetemplate = array('text' => '', 'format' => FORMAT_HTML);
@@ -150,4 +156,87 @@ class qtype_essay_test_helper extends question_test_helper {
$q->responsetemplateformat = FORMAT_HTML;
return $q;
}
+
+ /**
+ * Makes an essay question without an inline text editor.
+ * @return qtype_essay_question
+ */
+ public function make_essay_question_noinline() {
+ $q = $this->initialise_essay_question();
+ $q->responseformat = 'noinline';
+ $q->attachments = 3;
+ $q->attachmentsrequired = 1;
+ return $q;
+ }
+
+ /**
+ * Creates an empty draft area for attachments.
+ * @return int The draft area's itemid.
+ */
+ protected function make_attachment_draft_area() {
+ $draftid = 0;
+ $contextid = 0;
+
+ $component = 'question';
+ $filearea = 'response_attachments';
+
+ // Create an empty file area.
+ file_prepare_draft_area($draftid, $contextid, $component, $filearea, null);
+ return $draftid;
+ }
+
+ /**
+ * Creates an attachment in the provided attachment draft area.
+ * @param int $draftid The itemid for the draft area in which the file should be created.
+ * @param string $name The filename for the file to be created.
+ * @param string $contents The contents of the file to be created.
+ */
+ protected function make_attachment($draftid, $name, $contents) {
+ global $USER;
+
+ $fs = get_file_storage();
+ $usercontext = context_user::instance($USER->id);
+
+ // Create the file in the provided draft area.
+ $fileinfo = array(
+ 'contextid' => $usercontext->id,
+ 'component' => 'user',
+ 'filearea' => 'draft',
+ 'itemid' => $draftid,
+ 'filepath' => '/',
+ 'filename' => $name,
+ );
+ $fs->create_file_from_string($fileinfo, $contents);
+ }
+
+ /**
+ * Generates a draft file area that contains the provided number of attachments. You should ensure
+ * that a user is logged in with setUser before you run this function.
+ *
+ * @param int $attachments The number of attachments to generate.
+ * @return int The itemid of the generated draft file area.
+ */
+ public function make_attachments($attachments) {
+ $draftid = $this->make_attachment_draft_area();
+
+ // Create the relevant amount of dummy attachments in the given draft area.
+ for ($i = 0; $i < $attachments; ++$i) {
+ $this->make_attachment($draftid, $i, $i);
+ }
+
+ return $draftid;
+ }
+
+ /**
+ * Generates a question_file_saver that contains the provided number of attachments. You should ensure
+ * that a user is logged in with setUser before you run this function.
+ *
+ * @param int $:attachments The number of attachments to generate.
+ * @return question_file_saver a question_file_saver that contains the given amount of dummy files, for use in testing.
+ */
+ public function make_attachments_saver($attachments) {
+ return new question_file_saver($this->make_attachments($attachments), 'question', 'response_attachments');
+ }
+
+
}
diff --git a/question/type/essay/tests/question_test.php b/question/type/essay/tests/question_test.php
index bd9cdb732e2..6ab9635e382 100644
--- a/question/type/essay/tests/question_test.php
+++ b/question/type/essay/tests/question_test.php
@@ -36,7 +36,7 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class qtype_essay_question_testcase extends advanced_testcase {
+class qtype_essay_question_test extends advanced_testcase {
public function test_get_question_summary() {
$essay = test_question_maker::make_an_essay_question();
$essay->questiontext = 'Hello
';
@@ -139,11 +139,29 @@ class qtype_essay_question_testcase extends advanced_testcase {
}
public function test_is_complete_response() {
+ $this->resetAfterTest(true);
+ // Create a new logged-in user, so we can test responses with attachments.
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
+
+ // Create sample attachments to use in testing.
+ $helper = test_question_maker::get_test_helper('essay');
+ $attachments = array();
+ for ($i = 0; $i < 4; ++$i) {
+ $attachments[$i] = $helper->make_attachments_saver($i);
+ }
+
+ // Create the essay question under test.
$essay = test_question_maker::make_an_essay_question();
$essay->start_attempt(new question_attempt_step(), 1);
- // The empty string should be considered an empty response, as should a lack of a response.
+ // Test the "traditional" case, where we must recieve a response from the user.
+ $essay->responserequired = 1;
+ $essay->attachmentsrequired = 0;
+ $essay->responseformat = 'editor';
+
+ // The empty string should be considered an incomplete response, as should a lack of a response.
$this->assertFalse($essay->is_complete_response(array('answer' => '')));
$this->assertFalse($essay->is_complete_response(array()));
@@ -151,5 +169,74 @@ class qtype_essay_question_testcase extends advanced_testcase {
$this->assertTrue($essay->is_complete_response(array('answer' => 'A student response.')));
$this->assertTrue($essay->is_complete_response(array('answer' => '0 times.')));
$this->assertTrue($essay->is_complete_response(array('answer' => '0')));
+
+ // Test the case where two files are required.
+ $essay->attachmentsrequired = 2;
+
+ // Attaching less than two files should result in an incomplete response.
+ $this->assertFalse($essay->is_complete_response(array('answer' => 'A')));
+ $this->assertFalse($essay->is_complete_response(
+ array('answer' => 'A', 'attachments' => $attachments[0])));
+ $this->assertFalse($essay->is_complete_response(
+ array('answer' => 'A', 'attachments' => $attachments[1])));
+
+ // Anything without response text should result in an incomplete response.
+ $this->assertFalse($essay->is_complete_response(
+ array('answer' => '', 'attachments' => $attachments[2])));
+
+ // Attaching two or more files should result in a complete response.
+ $this->assertTrue($essay->is_complete_response(
+ array('answer' => 'A', 'attachments' => $attachments[2])));
+ $this->assertTrue($essay->is_complete_response(
+ array('answer' => 'A', 'attachments' => $attachments[3])));
+
+ // Test the case in which two files are required, but the inline
+ // response is optional.
+ $essay->responserequired = 0;
+
+ $this->assertFalse($essay->is_complete_response(
+ array('answer' => '', 'attachments' => $attachments[1])));
+
+ $this->assertTrue($essay->is_complete_response(
+ array('answer' => '', 'attachments' => $attachments[2])));
+
+ // Test the case in which both the response and inline text are optional.
+ $essay->attachmentsrequired = 0;
+
+ // Providing no answer and no attachment should result in an incomplete
+ // response.
+ $this->assertFalse($essay->is_complete_response(
+ array('answer' => '')));
+ $this->assertFalse($essay->is_complete_response(
+ array('answer' => '', 'attachments' => $attachments[0])));
+
+ // Providing an answer _or_ an attachment should result in a complete
+ // response.
+ $this->assertTrue($essay->is_complete_response(
+ array('answer' => '', 'attachments' => $attachments[1])));
+ $this->assertTrue($essay->is_complete_response(
+ array('answer' => 'Answer text.', 'attachments' => $attachments[0])));
+
+ // Test the case in which we're in "no inline response" mode,
+ // in which the response is not required (as it's not provided).
+ $essay->reponserequired = 0;
+ $essay->responseformat = 'noinline';
+ $essay->attachmensrequired = 1;
+
+ $this->assertFalse($essay->is_complete_response(
+ array()));
+ $this->assertFalse($essay->is_complete_response(
+ array('attachments' => $attachments[0])));
+
+ // Providing an attachment should result in a complete response.
+ $this->assertTrue($essay->is_complete_response(
+ array('attachments' => $attachments[1])));
+
+ // Ensure that responserequired is ignored when we're in inline response mode.
+ $essay->reponserequired = 1;
+ $this->assertTrue($essay->is_complete_response(
+ array('attachments' => $attachments[1])));
+
}
+
}
diff --git a/question/type/essay/version.php b/question/type/essay/version.php
index b079d4b9e18..28571bded95 100644
--- a/question/type/essay/version.php
+++ b/question/type/essay/version.php
@@ -26,7 +26,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'qtype_essay';
-$plugin->version = 2013110500;
+$plugin->version = 2014011301;
$plugin->requires = 2013110500;