MDL-20636 Essay questions can now handle files in the HTML editor. #216
authorTim Hunt <T.J.Hunt@open.ac.uk>
Fri, 18 Mar 2011 16:39:31 +0000 (16:39 +0000)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Thu, 31 Mar 2011 11:45:36 +0000 (12:45 +0100)
question/engine/datalib.php
question/engine/lib.php
question/type/essay/question.php
question/type/essay/renderer.php
question/type/essay/styles.css

index 5013b35..e961757 100644 (file)
@@ -119,15 +119,15 @@ class question_engine_data_mapper {
         $record->id = $this->db->insert_record('question_attempt_steps', $record);
 
         foreach ($step->get_all_data() as $name => $value) {
+            if ($value instanceof question_file_saver) {
+                $value->save_files($record->id, $context);
+            }
+
             $data = new stdClass();
             $data->attemptstepid = $record->id;
             $data->name = $name;
             $data->value = $value;
             $this->db->insert_record('question_attempt_step_data', $data, false);
-
-            if ($value instanceof question_file_saver) {
-                $value->save_files($record->id, $context);
-            }
         }
     }
 
@@ -1022,24 +1022,27 @@ class question_file_saver {
      * @param string $component the component for the file area to save into.
      * @param string $filearea the name of the file area to save into.
      */
-    public function __construct($draftitemid, $component, $filearea) {
+    public function __construct($draftitemid, $component, $filearea, $text = null) {
         $this->draftitemid = $draftitemid;
         $this->component = $component;
         $this->filearea = $filearea;
+        $this->value = $this->compute_value($draftitemid, $text);
     }
 
-    protected function get_value() {
+    /**
+     * Compute the value that should be stored in the question_attempt_step_data
+     * table. Contains a hash that (almost) uniquely encodes all the files.
+     * @param int $draftitemid the draft file area itemid.
+     * @param string $text optional content containing file links.
+     */
+    protected function compute_value($draftitemid, $text) {
         global $USER;
 
-        if (!is_null($this->value)) {
-            return $this->value;
-        }
-
         $fs = get_file_storage();
         $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 
         $files = $fs->get_area_files($usercontext->id, 'user', 'draft',
-                $this->draftitemid, 'sortorder, filepath, filename', false);
+                $draftitemid, 'sortorder, filepath, filename', false);
 
         $string = '';
         foreach ($files as $file) {
@@ -1048,16 +1051,27 @@ class question_file_saver {
         }
 
         if ($string) {
-            $this->value = md5($string);
+            $hash = md5($string);
         } else {
-            $this->value = '';
+            $hash = '';
         }
 
-        return $this->value;
+        if (is_null($text)) {
+            return $hash;
+        }
+
+        // We add the file hash so a simple string comparison will say if the
+        // files have been changed. First strip off any existing file hash.
+        $text = preg_replace('/\s*<!-- File hash: \w+ -->\s*$/', '', $text);
+        $text = file_rewrite_urls_to_pluginfile($text, $draftitemid);
+        if ($hash) {
+            $text .= '<!-- File hash: ' . $hash . ' -->';
+        }
+        return $text;
     }
 
     public function __toString() {
-        return $this->get_value();
+        return $this->value;
     }
 
     /**
index c517660..141edfc 100644 (file)
@@ -1347,6 +1347,12 @@ class question_attempt {
      */
     const PARAM_FILES = 'paramfiles';
 
+    /**
+     * @var string special value to indicate a response variable that is uploaded
+     * files.
+     */
+    const PARAM_CLEANHTML_FILES = 'paramcleanhtmlfiles';
+
     /** @var integer if this attempts is stored in the question_attempts table, the id of that row. */
     protected $id = null;
 
@@ -1661,6 +1667,21 @@ class question_attempt {
         return $default;
     }
 
+    /**
+     * Get the last step with a particular question type varialbe set.
+     * @param string $name the name of the variable to get.
+     * @return question_attempt_step the last step, or a step with no variables
+     * if there was not a real step.
+     */
+    public function get_last_step_with_qt_var($name) {
+        foreach ($this->get_reverse_step_iterator() as $step) {
+            if ($step->has_qt_var($name)) {
+                return $step;
+            }
+        }
+        return new question_attempt_step_read_only();
+    }
+
     /**
      * Get the latest value of a particular question type variable. That is, get
      * the value from the latest step that has it set. Return null if it is not
@@ -1672,12 +1693,12 @@ class question_attempt {
      * @return mixed string value, or $default if it has never been set.
      */
     public function get_last_qt_var($name, $default = null) {
-        foreach ($this->get_reverse_step_iterator() as $step) {
-            if ($step->has_qt_var($name)) {
-                return $step->get_qt_var($name);
-            }
+        $step = $this->get_last_step_with_qt_var($name);
+        if ($step->has_qt_var($name)) {
+            return $step->get_qt_var($name);
+        } else {
+            return $default;
         }
-        return $default;
     }
 
     /**
@@ -1890,6 +1911,15 @@ class question_attempt {
         return $this->behaviour->summarise_action($step);
     }
 
+    /**
+     * Helper function used by {@link rewrite_pluginfile_urls()} and
+     * {@link rewrite_response_pluginfile_urls()}.
+     * @return array ids that need to go into the file paths.
+     */
+    protected function extra_file_path_components() {
+        return array($this->get_usage_id(), $this->get_slot());
+    }
+
     /**
      * Calls {@link question_rewrite_question_urls()} with appropriate parameters
      * for content belonging to this question.
@@ -1897,11 +1927,28 @@ class question_attempt {
      * @param string $component the component name (normally 'question' or 'qtype_...')
      * @param string $filearea the name of the file area.
      * @param int $itemid the item id.
+     * @return srting the content with the URLs rewritten.
      */
     public function rewrite_pluginfile_urls($text, $component, $filearea, $itemid) {
-        return question_rewrite_question_urls($text,
-                'pluginfile.php', $this->question->contextid, $component, $filearea,
-                array($this->get_usage_id(), $this->get_slot()), $itemid);
+        return question_rewrite_question_urls($text, 'pluginfile.php',
+                $this->question->contextid, $component, $filearea,
+                $this->extra_file_path_components(), $itemid);
+    }
+
+    /**
+     * Calls {@link question_rewrite_question_urls()} with appropriate parameters
+     * for content belonging to responses to this question.
+     *
+     * @param string $text the text to update the URLs in.
+     * @param int $contextid the id of the context the quba belongs to.
+     * @param string $name the variable name the files belong to.
+     * @param question_attempt_step $step the step the response is coming from.
+     * @return srting the content with the URLs rewritten.
+     */
+    public function rewrite_response_pluginfile_urls($text, $contextid, $name,
+            question_attempt_step $step) {
+        return $step->rewrite_response_pluginfile_urls($text, $contextid, $name,
+                $this->extra_file_path_components());
     }
 
     /**
@@ -2043,61 +2090,66 @@ class question_attempt {
      * {@link optional_param()}, except that the results is returned without
      * slashes.
      * @param string $name the paramter name.
-     * @param int $type one of the PARAM_... constants.
+     * @param int $type one of the standard PARAM_... constants, or one of the
+     *      special extra constands defined by this class.
      * @param array $postdata (optional, only inteded for testing use) take the
      *      data from this array, instead of from $_POST.
      * @return mixed the requested value.
      */
     public function get_submitted_var($name, $type, $postdata = null) {
-        // Special case to work around PARAM_NUMBER converting '' to 0.
-        if ($type == self::PARAM_MARK) {
-            $mark = $this->get_submitted_var($name, PARAM_RAW_TRIMMED, $postdata);
-            if ($mark === '') {
-                return $mark;
-            } else {
-                return $this->get_submitted_var($name, PARAM_NUMBER, $postdata);
-            }
-        }
+        switch ($type) {
+            case self::PARAM_MARK:
+                // Special case to work around PARAM_NUMBER converting '' to 0.
+                $mark = $this->get_submitted_var($name, PARAM_RAW_TRIMMED, $postdata);
+                if ($mark === '') {
+                    return $mark;
+                } else {
+                    return $this->get_submitted_var($name, PARAM_NUMBER, $postdata);
+                }
 
-        if ($type == self::PARAM_FILES) {
-            return $this->process_response_files($name, $postdata);
-        }
+            case self::PARAM_FILES:
+                return $this->process_response_files($name, $name, $postdata);
 
-        if (is_null($postdata)) {
-            $var = optional_param($name, null, $type);
-        } else if (array_key_exists($name, $postdata)) {
-            $var = clean_param($postdata[$name], $type);
-        } else {
-            $var = null;
-        }
+            case self::PARAM_CLEANHTML_FILES:
+                $var = $this->get_submitted_var($name, PARAM_CLEANHTML, $postdata);
+                return $this->process_response_files($name, $name . ':itemid', $postdata, $var);
 
-        if (is_string($var)) {
-            $var = $var;
-        }
+            default:
+                if (is_null($postdata)) {
+                    $var = optional_param($name, null, $type);
+                } else if (array_key_exists($name, $postdata)) {
+                    $var = clean_param($postdata[$name], $type);
+                } else {
+                    $var = null;
+                }
 
-        return $var;
+                return $var;
+        }
     }
 
     /**
      * Handle a submitted variable representing uploaded files.
      * @param string $name the field name.
+     * @param string $draftidname the field name holding the draft file area id.
      * @param array $postdata (optional, only inteded for testing use) take the
      *      data from this array, instead of from $_POST. At the moment, this
      *      behaves as if there were no files.
+     * @param string $text optional reponse text.
+     * @return question_file_saver that can be used to save the files later.
      */
-    protected function process_response_files($name, $postdata = null) {
+    protected function process_response_files($name, $draftidname, $postdata = null, $text = null) {
         if ($postdata) {
             // There can be no files with test data (at the moment).
             return null;
         }
 
-        $draftitemid = file_get_submitted_draft_itemid($name);
+        $draftitemid = file_get_submitted_draft_itemid($draftidname);
         if (!$draftitemid) {
             return null;
         }
 
         return new question_file_saver($draftitemid, 'question', 'response_' .
-                str_replace($this->get_field_prefix(), '', $name));
+                str_replace($this->get_field_prefix(), '', $name), $text);
     }
 
     /**
@@ -2612,10 +2664,6 @@ class question_attempt_step {
         }
     }
 
-    public function get_id() {
-        return $this->id; // TODO get rid of this.
-    }
-
     /** @return question_state The state after this step. */
     public function get_state() {
         return $this->state;
@@ -2719,12 +2767,44 @@ class question_attempt_step {
      * @return int the draft itemid.
      */
     public function prepare_response_files_draft_itemid($name, $contextid) {
-    $draftid = 0; // Will be filled in by file_prepare_draft_area.
-        file_prepare_draft_area($draftid, $contextid, 'question',
-                'response_' . $name, $this->id);
+        list($draftid, $notused) = $this->prepare_response_files_draft_itemid_with_text(
+                $name, $contextid, null);
         return $draftid;
     }
 
+    /**
+     * Prepare a draft file are for the files belonging the a response variable
+     * of this step, while rewriting the URLs in some text.
+     *
+     * @param string $name the variable name the files belong to.
+     * @param int $contextid the id of the context the quba belongs to.
+     * @param string $text the text to update the URLs in.
+     * @return array(int, string) the draft itemid and the text with URLs rewritten.
+     */
+    public function prepare_response_files_draft_itemid_with_text($name, $contextid, $text) {
+        $draftid = 0; // Will be filled in by file_prepare_draft_area.
+        $newtext = file_prepare_draft_area($draftid, $contextid, 'question',
+                'response_' . $name, $this->id, null, $text);
+        return array($draftid, $newtext);
+    }
+
+    /**
+     * Rewrite the @@PLUGINFILE@@ tokens in a response variable from this step
+     * that contains links to file. Normally you should probably call
+     * {@link question_attempt::rewrite_response_pluginfile_urls()} instead of
+     * calling this method directly.
+     *
+     * @param string $text the text to update the URLs in.
+     * @param int $contextid the id of the context the quba belongs to.
+     * @param string $name the variable name the files belong to.
+     * @param array $extra extra file path components.
+     * @return string the rewritten text.
+     */
+    public function rewrite_response_pluginfile_urls($text, $contextid, $name, $extras) {
+        return question_rewrite_question_urls($text, 'pluginfile.php', $contextid,
+                'question', 'response_' . $name, $extras, $this->id);
+    }
+
     /**
      * Get all the question type variables.
      * @param array name => value pairs.
index 3f730cd..92d2070 100644 (file)
@@ -55,7 +55,12 @@ class qtype_essay_question extends question_with_responses {
     }
 
     public function get_expected_data() {
-        $expecteddata = array('answer' => PARAM_CLEANHTML, 'answerformat' => PARAM_FORMAT);
+        if ($this->responseformat == 'editorfilepicker') {
+            $expecteddata = array('answer' => question_attempt::PARAM_CLEANHTML_FILES);
+        } else {
+            $expecteddata = array('answer' => PARAM_CLEANHTML);
+        }
+        $expecteddata['answerformat'] = PARAM_FORMAT;
         if ($this->attachments != 0) {
             $expecteddata['attachments'] = question_attempt::PARAM_FILES;
         }
@@ -92,6 +97,10 @@ class qtype_essay_question extends question_with_responses {
             // Response attachments visible if the question has them.
             return $this->attachments != 0;
 
+        } else if ($component == 'question' && $filearea == 'response_answer') {
+            // Response attachments visible if the question has them.
+            return $this->responseformat === 'editorfilepicker';
+
         } else if ($component == 'qtype_essay' && $filearea == 'graderinfo') {
             return $options->manualcomment;
 
index c2d6fb8..684a30c 100644 (file)
@@ -42,17 +42,14 @@ class qtype_essay_renderer extends qtype_renderer {
         $responseoutput = $question->get_format_renderer($this->page);
 
         // Answer field.
-        $inputname = $qa->get_qt_field_name('answer');
-        $response = $qa->get_last_qt_var('answer', '');
-        $responseformat = $qa->get_last_qt_var('answerformat', FORMAT_HTML);
+        $step = $qa->get_last_step_with_qt_var('answer');
         if (empty($options->readonly)) {
-            $answer = $responseoutput->response_area_input($inputname,
-                    $response, $responseformat, $question->responsefieldlines,
-                    $options->context);
+            $answer = $responseoutput->response_area_input('answer', $qa,
+                    $step, $question->responsefieldlines, $options->context);
 
         } else {
-            $answer = $responseoutput->response_area_read_only($inputname,
-                    $response, $responseformat, $question->responsefieldlines);
+            $answer = $responseoutput->response_area_read_only('answer', $qa,
+                    $step, $question->responsefieldlines, $options->context);
         }
 
         $files = '';
@@ -114,6 +111,9 @@ class qtype_essay_renderer extends qtype_renderer {
                 'attachments', $options->context->id);
         $pickeroptions->context = $options->context;
 
+        $pickeroptions->itemid = $qa->prepare_response_files_draft_itemid(
+                'attachments', $options->context->id);
+
         return form_filemanager_render($pickeroptions) . html_writer::empty_tag(
                 'input', array('type' => 'hidden', 'name' => $qa->get_qt_field_name('attachments'),
                 'value' => $pickeroptions->itemid));
@@ -142,21 +142,32 @@ class qtype_essay_renderer extends qtype_renderer {
 abstract class qtype_essay_format_renderer_base extends plugin_renderer_base {
     /**
      * Render the students respone when the question is in read-only mode.
-     * @param string $inputname the field name to use for this input.
-     * @param string $response the student's current response.
+     * @param string $name the variable name this input edits.
+     * @param question_attempt $qa the question attempt being display.
+     * @param question_attempt_step $step the current step.
      * @param int $lines approximate size of input box to display.
+     * @param object $context the context teh output belongs to.
+     * @return string html to display the response.
      */
-    public abstract function response_area_read_only($inputname, $response,
-            $responseformat, $lines);
+    public abstract function response_area_read_only($name, question_attempt $qa,
+            question_attempt_step $step, $lines, $context);
 
     /**
      * Render the students respone when the question is in read-only mode.
-     * @param string $inputname the field name to use for this input.
-     * @param string $response the student's current response.
+     * @param string $name the variable name this input edits.
+     * @param question_attempt $qa the question attempt being display.
+     * @param question_attempt_step $step the current step.
      * @param int $lines approximate size of input box to display.
+     * @param object $context the context teh output belongs to.
+     * @return string html to display the response for editing.
+     */
+    public abstract function response_area_input($name, question_attempt $qa,
+            question_attempt_step $step, $lines, $context);
+
+    /**
+     * @return string specific class name to add to the input element.
      */
-    public abstract function response_area_input($inputname, $response,
-            $responseformat, $lines, $contex);
+    protected abstract function class_name();
 }
 
 
@@ -168,18 +179,21 @@ abstract class qtype_essay_format_renderer_base extends plugin_renderer_base {
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class qtype_essay_format_editor_renderer extends plugin_renderer_base {
-    public function response_area_read_only($inputname, $response, $responseformat, $lines) {
-        $formatoptions = new stdClass();
-        $formatoptions->para = false;
-        $response = $this->rewrite_pluginfile_urls($response);
-        return html_writer::tag('div', format_text($response, $responseformat, $formatoptions),
-                array('class' => 'qtype_essay_editor qtype_essay_response'));
+    protected function class_name() {
+        return 'qtype_essay_editor';
     }
 
-    public function response_area_input($inputname, $response, $responseformat, $lines, $context) {
+    public function response_area_read_only($name, $qa, $step, $lines, $context) {
+        return html_writer::tag('div', $this->prepare_response($name, $qa, $step, $context),
+                array('class' => $this->class_name() . ' qtype_essay_response'));
+    }
+
+    public function response_area_input($name, $qa, $step, $lines, $context) {
         global $CFG, $PAGE;
         require_once($CFG->dirroot.'/repository/lib.php');
 
+        $inputname = $qa->get_qt_field_name($name);
+        $responseformat = $step->get_qt_var($name . 'format');
         $id = $inputname . '_id';
 
         $editor = editors_get_preferred_editor($responseformat);
@@ -189,13 +203,17 @@ class qtype_essay_format_editor_renderer extends plugin_renderer_base {
             $formats[$fid] = $strformats[$fid];
         }
 
+        list($draftitemid, $reponse) = $this->prepare_response_for_editing(
+                $name, $step, $context);
+
         $editor->use_editor($id, $this->get_editor_options($context),
-                $this->get_filepicker_options());
+                $this->get_filepicker_options($context, $draftitemid));
 
         $output = '';
-        $output .= html_writer::start_tag('div');
+        $output .= html_writer::start_tag('div', array('class' =>
+                $this->class_name() . ' qtype_essay_response'));
 
-        $output .= html_writer::tag('div', html_writer::tag('textarea', s($response),
+        $output .= html_writer::tag('div', html_writer::tag('textarea', s($reponse),
                 array('id' => $id, 'name' => $inputname, 'rows' => $lines)));
 
         $output .= html_writer::start_tag('div');
@@ -209,28 +227,46 @@ class qtype_essay_format_editor_renderer extends plugin_renderer_base {
         }
         $output .= html_writer::end_tag('div');
 
-        $output .= $this->nonjs_filepicker();
+        $output .= $this->filepicker_html($inputname, $draftitemid);
 
         $output .= html_writer::end_tag('div');
         return $output;
     }
 
     /**
-     * @param string $response the student's response.
-     * @return string the response with file URLs processed.
+     * Prepare the response for read-only display.
+     * @param string $name the variable name this input edits.
+     * @param question_attempt $qa the question attempt being display.
+     * @param question_attempt_step $step the current step.
+     * @param object $context the context the attempt belongs to.
+     * @return string the response prepared for display.
      */
-    protected function rewrite_pluginfile_urls($response) {
-        return $response;
+    protected function prepare_response($name, question_attempt $qa,
+            question_attempt_step $step, $context) {
+        if (!$step->has_qt_var($name)) {
+            return '';
+        }
+
+        $formatoptions = new stdClass();
+        $formatoptions->para = false;
+        return format_text($step->get_qt_var($name), $step->get_qt_var($name . 'format'),
+                $formatoptions);
     }
 
     /**
-     * @return array filepicker options for the editor.
+     * Prepare the response for editing.
+     * @param string $name the variable name this input edits.
+     * @param question_attempt_step $step the current step.
+     * @param object $context the context the attempt belongs to.
+     * @return string the response prepared for display.
      */
-    protected function get_filepicker_options() {
-        return array();
+    protected function prepare_response_for_editing($name,
+            question_attempt_step $step, $context) {
+        return array(0, $step->get_qt_var($name));
     }
 
     /**
+     * @param object $context the context the attempt belongs to.
      * @return array options for the editor.
      */
     protected function get_editor_options($context) {
@@ -238,9 +274,20 @@ class qtype_essay_format_editor_renderer extends plugin_renderer_base {
     }
 
     /**
-     * Extra output for the filepicker, if used.
+     * @param object $context the context the attempt belongs to.
+     * @param int $draftitemid draft item id.
+     * @return array filepicker options for the editor.
+     */
+    protected function get_filepicker_options($context, $draftitemid) {
+        return array();
+    }
+
+    /**
+     * @param string $inputname input field name.
+     * @param int $draftitemid draft file area itemid.
+     * @return string HTML for the filepicker, if used.
      */
-    protected function nonjs_filepicker() {
+    protected function filepicker_html($inputname, $draftitemid) {
         return '';
     }
 }
@@ -254,17 +301,93 @@ class qtype_essay_format_editor_renderer extends plugin_renderer_base {
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class qtype_essay_format_editorfilepicker_renderer extends qtype_essay_format_editor_renderer {
+    protected function class_name() {
+        return 'qtype_essay_editorfilepicker';
+    }
+
+    protected function prepare_response($name, question_attempt $qa,
+            question_attempt_step $step, $context) {
+        if (!$step->has_qt_var($name)) {
+            return '';
+        }
 
-    protected function rewrite_pluginfile_urls($response) {
-        return $response;
+        $formatoptions = new stdClass();
+        $formatoptions->para = false;
+        $text = $qa->rewrite_response_pluginfile_urls($step->get_qt_var($name),
+                $context->id, 'answer', $step);
+        return format_text($text, $step->get_qt_var($name . 'format'), $formatoptions);
     }
 
-    protected function get_filepicker_options() {
-        return array();
+    protected function prepare_response_for_editing($name,
+            question_attempt_step $step, $context) {
+        return $step->prepare_response_files_draft_itemid_with_text(
+                $name, $context->id, $step->get_qt_var($name));
     }
 
-    protected function nonjs_filepicker() {
-        return '';
+    protected function get_editor_options($context) {
+        return array(
+            'subdirs' => 0,
+            'maxbytes' => 0,
+            'maxfiles' => -1,
+            'context' => $context,
+            'noclean' => 0,
+            'trusttext'=>0
+        );
+    }
+
+    /**
+     * Get the options required to configure the filepicker for one of the editor
+     * toolbar buttons.
+     * @param mixed $acceptedtypes array of types of '*'.
+     * @param int $draftitemid the draft area item id.
+     * @param object $context the context.
+     * @return object the required options.
+     */
+    protected function specific_filepicker_options($acceptedtypes, $draftitemid, $context) {
+        $filepickeroptions = new stdClass();
+        $filepickeroptions->accepted_types = $acceptedtypes;
+        $filepickeroptions->return_types = FILE_INTERNAL | FILE_EXTERNAL;
+        $filepickeroptions->context = $context;
+        $filepickeroptions->env = 'filepicker';
+
+        $options = initialise_filepicker($filepickeroptions);
+        $options->context = $context;
+        $options->client_id = uniqid();
+        $options->env = 'editor';
+        $options->itemid = $draftitemid;
+
+        return $options;
+    }
+
+    protected function get_filepicker_options($context, $draftitemid) {
+        global $CFG;
+
+        return array(
+            'image' => $this->specific_filepicker_options(array('image'),
+                            $draftitemid, $context),
+            'media' => $this->specific_filepicker_options(array('video', 'audio'),
+                            $draftitemid, $context),
+            'link'  => $this->specific_filepicker_options('*',
+                            $draftitemid, $context),
+        );
+    }
+
+    protected function filepicker_html($inputname, $draftitemid) {
+        $nonjspickerurl = new moodle_url('/repository/draftfiles_manager.php', array(
+            'action' => 'browse',
+            'env' => 'editor',
+            'itemid' => $draftitemid,
+            'subdirs' => false,
+            'maxfiles' => -1,
+            'sesskey' => sesskey(),
+        ));
+
+        return html_writer::empty_tag('input', array('type' => 'hidden',
+                'name' => $inputname . ':itemid', 'value' => $draftitemid)) .
+                html_writer::tag('noscript', html_writer::tag('div',
+                    html_writer::tag('object', '', array('type' => 'text/html',
+                        'data' => $nonjspickerurl, 'height' => 160, 'width' => 600,
+                        'style' => 'border: 1px solid #000;'))));
     }
 }
 
@@ -290,12 +413,13 @@ class qtype_essay_format_plain_renderer extends plugin_renderer_base {
         return 'qtype_essay_plain';
     }
 
-    public function response_area_read_only($inputname, $response, $responseformat, $lines) {
-        return $this->textarea($response, $lines, array('readonly' => 'readonly'));
+    public function response_area_read_only($name, $qa, $step, $lines, $context) {
+        return $this->textarea($step->get_qt_var($name), $lines, array('readonly' => 'readonly'));
     }
 
-    public function response_area_input($inputname, $response, $responseformat, $lines, $contex) {
-        return $this->textarea($response, $lines, array('name' => $inputname)) .
+    public function response_area_input($name, $qa, $step, $lines, $context) {
+        $inputname = $qa->get_qt_field_name($name);
+        return $this->textarea($step->get_qt_var($name), $lines, array('name' => $inputname)) .
                 html_writer::empty_tag('input', array('type' => 'hidden',
                     'name' => $inputname . 'format', 'value' => FORMAT_PLAIN));
     }
index cf1c8c6..d7457fa 100644 (file)
@@ -9,7 +9,10 @@
     white-space: pre;
     font-family: Andale Mono, Monaco, Courier New, DejaVu Sans Mono, monospace;
 }
-.que.essay .qtype_essay_response.qtype_essay_editor {
+.que.essay .qtype_essay_response {
     background-color: white;
     min-height: 3em;
 }
+.que.essay div.qtype_essay_response textarea {
+    width: 100%;
+}