MDL-35053 questions: question files outside an attempt.
authorTim Hunt <T.J.Hunt@open.ac.uk>
Wed, 17 Jul 2013 18:46:27 +0000 (19:46 +0100)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Fri, 26 Jul 2013 12:10:08 +0000 (13:10 +0100)
Sometimes it is necssary to display parts of a question outside of an
attempt. For example for staff review. When displaying the question, we
need to handle images in the question text. In the past there was a
mechanism for this that could only cope with the question text.

This commit introduces a new method that can handle displaying any
part of the question content.

This commit intentionally does not upgrade the parts of the system that
use the mecanism. That is so that this commit can be used to demonstrate
that backwards-compatibility works. The next commit will upgrade the
callers.

lib/questionlib.php
question/upgrade.txt [new file with mode: 0644]

index 2a464d0..c61beac 100644 (file)
@@ -1731,21 +1731,60 @@ function question_rewrite_question_urls($text, $file, $contextid, $component,
 
 /**
  * Rewrite the PLUGINFILE urls in the questiontext, when viewing the question
- * text outside and attempt (for example, in the question bank listing or in the
+ * text outside an attempt (for example, in the question bank listing or in the
  * quiz statistics report).
  *
  * @param string $questiontext the question text.
  * @param int $contextid the context the text is being displayed in.
  * @param string $component component
- * @param array $ids other IDs will be used to check file permission
- * @param array $options
+ * @param array $questionid the question id
+ * @param array $options e.g. forcedownload. Passed to file_rewrite_pluginfile_urls.
  * @return string $questiontext with URLs rewritten.
+ * @deprecated since Moodle 2.6
  */
 function question_rewrite_questiontext_preview_urls($questiontext, $contextid,
         $component, $questionid, $options=null) {
+    global $DB;
+
+    debugging('question_rewrite_questiontext_preview_urls has been deprecated. ' .
+            'Please use question_rewrite_question_preview_urls instead', DEBUG_DEVELOPER);
+    $questioncontextid = $DB->get_field_sql('
+            SELECT qc.contextid
+              FROM {question} q
+              JOIN {question_categories} qc ON qc.id = q.category
+             WHERE q.id = :id', array('id' => $questionid), MUST_EXIST);
+    
+    return question_rewrite_question_preview_urls($questiontext, $questioncontextid,
+            'question', 'questiontext', $contextid, $component, $questionid, $options);
+}
+
+/**
+ * Rewrite the PLUGINFILE urls in part of the content of a question, for use when
+ * viewing the question outside an attempt (for example, in the question bank
+ * listing or in the quiz statistics report).
+ *
+ * @param string $text the question text.
+ * @param int $questionid the question id.
+ * @param int $filecontextid the context id of the question being displayed.
+ * @param string $filecomponent the component that owns the file area.
+ * @param string $filearea the file area name.
+ * @param int|null $itemid the file's itemid
+ * @param int $previewcontextid the context id where the preview is being displayed.
+ * @param string $previewcomponent component responsible for displaying the preview.
+ * @param array $options text and file options ('forcehttps'=>false)
+ * @return string $questiontext with URLs rewritten.
+ */
+function question_rewrite_question_preview_urls($text, $questionid,
+        $filecontextid, $filecomponent, $filearea, $itemid,
+        $previewcontextid, $previewcomponent, $options = null) {
 
-    return file_rewrite_pluginfile_urls($questiontext, 'pluginfile.php', $contextid,
-            'question', 'questiontext_preview', "$component/$questionid", $options);
+    $path = "preview/$previewcontextid/$previewcomponent/$questionid";
+    if ($itemid) {
+        $path .= '/' . $itemid;
+    }
+
+    return file_rewrite_pluginfile_urls($text, 'pluginfile.php', $filecontextid,
+            $filecomponent, $filearea, $path, $options);
 }
 
 /**
@@ -1754,10 +1793,13 @@ function question_rewrite_questiontext_preview_urls($questiontext, $contextid,
  * @param array $args the remaining file arguments (file path).
  * @param bool $forcedownload whether the user must be forced to download the file.
  * @param array $options additional options affecting the file serving
+ * @deprecated since Moodle 2.6.
  */
 function question_send_questiontext_file($questionid, $args, $forcedownload, $options) {
     global $DB;
 
+    debugging('question_send_questiontext_file has been deprecated. It is no longer necessary. ' .
+            'You can now just use send_stored_file.', DEBUG_DEVELOPER);
     $question = $DB->get_record_sql('
             SELECT q.id, qc.contextid
               FROM {question} q
@@ -1799,16 +1841,7 @@ function question_send_questiontext_file($questionid, $args, $forcedownload, $op
 function question_pluginfile($course, $context, $component, $filearea, $args, $forcedownload, array $options=array()) {
     global $DB, $CFG;
 
-    if ($filearea === 'questiontext_preview') {
-        $component = array_shift($args);
-        $questionid = array_shift($args);
-
-        component_callback($component, 'questiontext_preview_pluginfile', array(
-                $context, $questionid, $args, $forcedownload, $options));
-
-        send_file_not_found();
-    }
-
+    // Special case, sending a question bank export.
     if ($filearea === 'export') {
         list($context, $course, $cm) = get_context_info_array($context->id);
         require_login($course, false, $cm);
@@ -1868,7 +1901,35 @@ function question_pluginfile($course, $context, $component, $filearea, $args, $f
         send_file($content, $filename, 0, 0, true, true, $qformat->mime_type());
     }
 
-    $qubaid = (int)array_shift($args);
+    // Normal case, a file belonging to a question.
+    $qubaidorpreview = array_shift($args);
+
+    // Two sub-cases: 1. A question being previewed outside an attempt/usage.
+    if ($qubaidorpreview === 'preview') {
+        $previewcontextid = (int)array_shift($args);
+        $previewcomponent = array_shift($args);
+        $questionid = (int) array_shift($args);
+        $previewcontext = context_helper::instance_by_id($previewcontextid);
+
+        $result = component_callback($previewcomponent, 'question_preview_pluginfile', array(
+                $previewcontext, $questionid,
+                $context, $component, $filearea, $args,
+                $forcedownload, $options), 'newcallbackmissing');
+
+        if ($result === 'newcallbackmissing' && $filearea = 'questiontext') {
+            // Fall back to the legacy callback for backwards compatibility.
+            debugging("Component {$previewcomponent} does not define the expected " .
+                    "{$previewcomponent}_question_preview_pluginfile callback. Falling back to the deprecated " .
+                    "{$previewcomponent}_questiontext_preview_pluginfile callback.", DEBUG_DEVELOPER);
+            component_callback($previewcomponent, 'questiontext_preview_pluginfile', array(
+                    $previewcontext, $questionid, $args, $forcedownload, $options));
+        }
+
+        send_file_not_found();
+    }
+
+    // 2. A question being attempted in the normal way.
+    $qubaid = (int)$qubaidorpreview;
     $slot = (int)array_shift($args);
 
     $module = $DB->get_field('question_usages', 'component',
diff --git a/question/upgrade.txt b/question/upgrade.txt
new file mode 100644 (file)
index 0000000..32ad8f2
--- /dev/null
@@ -0,0 +1,28 @@
+This files describes API changes for code that uses the question API.
+
+=== 2.6 ===
+
+1) It is sometimes necessary to display bits of question content without having
+   and attempt (question_usage) in progress. Two examples of this are the option
+   in the question bank to display the questiontext, and in the quiz statistics
+   report, where it displays the question text above the report.
+
+   Previously, this display was done using a special method that only worked for
+   the question text, but which would not work for other parts of the question.
+   That old mechanism has been deprecated, and there is a new method that you
+   should use.
+
+   To display the question, replace calls to question_rewrite_questiontext_preview_urls
+   with calls to question_rewrite_question_preview_urls. Because the new function
+   is more flexibile, you have to pass more arguments.
+
+   To perform the necessary permission checks when the file is downloaded, you need
+   to implement the callback [component name]_question_preview_pluginfile.
+   (Previously you implemented [component name]_questiontext_preview_pluginfile.)
+   quiz_statistics_question_preview_pluginfile is an example of what to do.
+
+   question_send_questiontext_file has been deprecated. It is no longer necessary.
+
+   To ensure you are no longer using or defining any deprecated functions,
+   search for the regular expression:
+   question_rewrite_questiontext_preview_urls|_questiontext_preview_pluginfile|question_send_questiontext_file