Skip to content
Snippets Groups Projects
Commit c342f814 authored by Matti Harjula's avatar Matti Harjula
Browse files

Excise all Moodle pluginfile related logic from the core, also collect other...

Excise all Moodle pluginfile related logic from the core, also collect other VLE-specific things in a single file that anyone porting STACK can simply replace. Or better yet we will add a config paramtert to load the one that matters on that platform.
parent b9097703
No related branches found
No related tags found
No related merge requests found
......@@ -34,6 +34,7 @@ require_once(__DIR__ . '/questiontype.php');
require_once(__DIR__ . '/stack/cas/secure_loader.class.php');
require_once(__DIR__ . '/stack/prt.class.php');
require_once(__DIR__ . '/stack/prt.evaluatable.class.php');
require_once(__DIR__ . '/vle_specific.php');
/**
* Represents a Stack question.
......@@ -1263,14 +1264,6 @@ class qtype_stack_question extends question_graded_automatically_with_countback
($USER->id == $this->createdby && has_capability("moodle/question:{$type}mine", $context));
}
public function user_can_view() {
return $this->has_question_capability('view');
}
public function user_can_edit() {
return $this->has_question_capability('edit');
}
/* Get the values of all variables which have a key. So, function definitions
* and assignments are ignored by this method. Used to display the values of
* variables used in a question variant. Beware that some functions have side
......@@ -1798,18 +1791,32 @@ class qtype_stack_question extends question_graded_automatically_with_countback
$cc['forbiddenkeys'] = $forbiddenkeys;
// Do some pluginfile mapping. Note that the PRT-nodes are mapped in PRT-compiler.
if (strpos($questiontext, '@@PLUGINFILE@@') !== false) {
$questiontext = '[[pfs component="question" filearea="questiontext" itemid="' . $id . '"]]' .
$questiontext . '[[/pfs]]';
}
if (strpos($generalfeedback, '@@PLUGINFILE@@') !== false) {
$generalfeedback = '[[pfs component="question" filearea="generalfeedback" itemid="' . $id . '"]]' .
$generalfeedback . '[[/pfs]]';
}
if (strpos($specificfeedback, '@@PLUGINFILE@@') !== false) {
$specificfeedback = '[[pfs component="qtype_stack" filearea="specificfeedback" itemid="' . $id . '"]]' .
$specificfeedback . '[[/pfs]]';
}
$questiontext = stack_castext_file_filter($questiontext, [
'questionid' => $id,
'field' => 'questiontext'
]);
$generalfeedback = stack_castext_file_filter($generalfeedback, [
'questionid' => $id,
'field' => 'generalfeedback'
]);
$specificfeedback = stack_castext_file_filter($specificfeedback, [
'questionid' => $id,
'field' => 'specificfeedback'
]);
$prtcorrect = stack_castext_file_filter($prtcorrect, [
'questionid' => $id,
'field' => 'prtcorrect'
]);
$prtpartiallycorrect = stack_castext_file_filter($prtpartiallycorrect, [
'questionid' => $id,
'field' => 'prtpartiallycorrect'
]);
$prtincorrect = stack_castext_file_filter($prtincorrect, [
'questionid' => $id,
'field' => 'prtincorrect'
]);
// Compile the castext fragments.
$ctoptions = [
'bound-vars' => $forbiddenkeys,
......
......@@ -24,6 +24,8 @@
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/vle_specific.php');
/**
* Generates the output for Stack questions.
*
......@@ -152,14 +154,14 @@ class qtype_stack_renderer extends qtype_renderer {
if (!empty($options->suppressruntestslink)) {
return '';
}
if (!$question->user_can_view()) {
if (!stack_user_can_view_question($question)) {
return '';
}
$urlparams = array('questionid' => $question->id);
$links = array();
if ($question->user_can_edit()) {
if (stack_user_can_edit_question($question)) {
$links[] = html_writer::link(
$question->qtype->get_tidy_question_url($question),
stack_string('tidyquestion'));
......
......@@ -16,6 +16,8 @@
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../../vle_specific.php');
/**
* Encapsulates the location of an error happening in CAS with the actual error.
* Allows us to decide the level of error message specificity at the point of output.
......@@ -208,7 +210,7 @@ class stack_cas_error {
// Order as you want and there are other vars available.
$ctx = $this->get_interpreted_context($question);
if ($question->user_can_edit($question)) {
if (stack_user_can_edit_question($question)) {
// Only editing people can have the error itself in the template.
$ctx['err'] = $this->error;
if (isset($ctx['prt'])) {
......
......@@ -19,7 +19,8 @@ defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/cas/ast.container.class.php');
require_once(__DIR__ . '/cas/keyval.class.php');
require_once(__DIR__ . '/cas/castext2/castext2_evaluatable.class.php');
require_once(__DIR__ . '/../stack/answertest/controller.class.php');
require_once(__DIR__ . '/answertest/controller.class.php');
require_once(__DIR__ . '/../vle_specific.php');
// Deals with whole potential response trees.
// A rewrite dropping everything not needed for compiled PRTs.
......@@ -30,6 +31,9 @@ class stack_potentialresponse_tree_lite {
/** @var string Name of the PRT. */
private $name;
/** @var id Identifier of the PRT. */
private $id;
/** @var bool Should this PRT simplify when its arguments are evaluated? */
private $simplify;
......@@ -70,6 +74,7 @@ class stack_potentialresponse_tree_lite {
public function __construct($prtdata, $value, $question = null) {
$this->name = $prtdata->name;
$this->id = $prtdata->id;
$this->simplify = (bool) $prtdata->autosimplify;
$this->feedbackstyle = (int) $prtdata->feedbackstyle;
......@@ -650,11 +655,12 @@ class stack_potentialresponse_tree_lite {
if ($node->truefeedback !== null && trim($node->truefeedback) !== '') {
// Note the space separates any feedback from that generated by the prt node.
$feedback = ' ' . $node->truefeedback;
if (strpos($feedback, '@@PLUGINFILE@@') !== false) {
$feedback = '[[pfs component="qtype_stack" filearea="prtnodetruefeedback" itemid="' .
$node->id . '"]]' . $feedback . '[[/pfs]]';
}
$feedback = ' ' . stack_castext_file_filter($node->truefeedback,
['field' => 'prtnodetruefeedback',
'prtnodeid' => $node->id,
'prtid' => $this->id, // For completeness sake.
'questionid' => $this->question->id
]);
if (substr($body, -1) !== '(') {
// Depends on whether the score math was done.
$body .= ',';
......@@ -736,11 +742,12 @@ class stack_potentialresponse_tree_lite {
if ($node->falsefeedback !== null && trim($node->falsefeedback) !== '') {
// Note the space separates any feedback from that generated by the prt node.
$feedback = ' ' . $node->falsefeedback;
if (strpos($feedback, '@@PLUGINFILE@@') !== false) {
$feedback = '[[pfs component="qtype_stack" filearea="prtnodefalsefeedback" itemid="' . $node->id . '"]]' .
$feedback . '[[/pfs]]';
}
$feedback = ' ' . stack_castext_file_filter($node->falsefeedback,
['field' => 'prtnodefalsefeedback',
'prtnodeid' => $node->id,
'prtid' => $this->id, // For completeness sake.
'questionid' => $this->question->id
]);
if (substr($body, -1) !== '(') { // Depends on whether the score math was done.
$body .= ',';
}
......
......@@ -23,6 +23,7 @@
*/
require_once(__DIR__ . '/../../../config.php');
require_once(__DIR__ . '/vle_specific.php');
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
......@@ -58,7 +59,7 @@ $qa = $dm->load_question_attempt($qaid);
$question = $qa->get_question();
$question->apply_attempt_state($qa->get_step(0));
if (!$question->user_can_view()) {
if (!stack_user_can_view_question($question)) {
header('HTTP/1.0 403 Forbidden');
header('Content-Type: text/plain;charset=UTF-8');
echo 'This question is not accessible for the active user';
......
<?php
// This file is part of Stack - http://stack.maths.ed.ac.uk/
//
// Stack 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.
//
// Stack 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 Stack. If not, see <http://www.gnu.org/licenses/>.
/**
* A collection of things that are a bit VLE specific and have been
* extracted from the general logic.
*
* If you are porting to another platform you should check these out
* these are not going to stop you from progressing but you will need
* these at some point.
*
* There are two main things here:
*
* 1. Permission checking, the future error message system will tune its
* verbosity based on whether the user is a teacher or not.
*
* 2. Attached file management, any links that need rewriting to
* access attached fiels should be handled by this. This is relevant
* for all bits of CASText.
*
*
* Elsewhere there are other major things:
*
* 1. The JSXGraph block in the CASText system uses JavaScript and loads
* it through the system, you will probably need to replace the block,
* should be enough to replace the portion of the block pushing out
* the script and the script itself may require some tuning related
* to the JavaScript Module system. If you don't want to support
* binding of inputs to JSXGraphs, just throw the block away.
*
* CASText blocks can be replaced during execution so you do not even
* need to touch the original file. Simply use the `register`-function
* in the block-factory to replace the class handling that particular
* block. Same logic can be used to add blocks if for example your file
* management would need a new one.
*
* 2. The inputs and their related JavaScripts, these are the difficult
* ones. Again replacing scripts and the loading logic for them can
* prove to be hard and you may even choose to live without
* the instant validation feature those scripts provide. Other than
* that the recommended way is to map whatever way you deal with
* $_POST or even $_GET data so that those inputs receive similar
* $response arrays as they would in Moodle. Mapping functions for
* dealing with the script handling would be a good idea, or dummy
* functions if one does not care about those.
*
* 3. Storage of the question, you can freely store thigns as you wish
* but it would be nice to have unique identifiers for all
* the things that the original Moodle database model has separated
* to tables. And naturally mapping to similar arrays/objects on
* on the code side will help.
*
*/
/**
* This answers the question whether the currently active user is
* able to edit this question. Basically, editing user.
*
* If you are unable to answer this question simply return FALSE.
*/
function stack_user_can_edit_question($question): bool {
// In Moodle we can get this directly from the question itself.
return $question->has_question_capability('edit');
}
/**
* This answers the question whether the currently active user is
* able to view this question. Basically, have it present in something
* that they are supposed to see.
*
* If you are unable to answer this question simply return TRUE.
* This is currently used for [[textdownload]] and being able to figure
* out a link to some other persons attempt is not really a problem.
*/
function stack_user_can_view_question($question): bool {
// In Moodle we can get this directly from the question itself.
return $question->has_question_capability('view');
}
/**
* Attachement files and CASText2 compilation note:
* 1. If your attacment url is entirelly static after the question
* has received its database IDs please write it open here.
* 2. In Moodle the url includes usage specific identifiers and must
* therefore be written open at the point of usage.
* 3. Due to that we use the [[pfs]]-block to carry relevant details
* around in the code so that the writing open step can access these
* details when need be and in Moodle the [[pfs]]-block does that.
* 4. If you have simillar needs for urls being specific to the user
* or usage crate your own block like [[pfs]] and register it to
* the CASText system to do the rewriting.
* 5! If you have that type of variance in handling you must not
* rewrite at this point as the result of these is stored as compiled
* CASText and will be used for all future users of this question.
* 6. You may need to deal with permissions here as well if you track
* access separately based on the part of the question the file
* exists in.
*
* 7. Note that rewriting to static urls during question import is also
* an option but it means that one needs to do more complex things
* during export if one wants to export those questiosn with those
* files.
*/
/**
* Rewrites or wraps in rewriting logic a given CASText string if it
* includes placeholders for urls that need to be rewritten.
*
* If your system does not support any such urls just return the string
* as is.
*/
function stack_castext_file_filter(string $castext, array $identifiers): string {
if ($castext === '') {
// Nothing to do with empty strings.
return $castext;
}
// In Moodle these are easy to spot.
if (mb_strpos($castext, '@@PLUGINFILE@@') !== false) {
// We use the PFS block that has been specicifally
// built for Moodle to pass on the relevant details.
$block = '[[pfs';
switch ($identifiers['field']) {
case 'questiontext':
case 'generalfeedback':
$block .= ' component="question"';
$block .= ' filearea="' . $identifiers['field'] . '"';
$block .= ' itemid="' . $identifiers['questionid'] . '"';
break;
case 'specificfeedback':
case 'prtcorrect': // These three are not in actual use
case 'prtpartiallycorrect':
case 'prtincorrect':
$block .= ' component="qtype_stack"';
$block .= ' filearea="' . $identifiers['field'] . '"';
$block .= ' itemid="' . $identifiers['questionid'] . '"';
break;
case 'prtnodetruefeedback':
case 'prtnodefalsefeedback':
$block .= ' component="qtype_stack"';
$block .= ' filearea="' . $identifiers['field'] . '"';
$block .= ' itemid="' . $identifiers['prtnodeid'] . '"';
break;
}
$block .= ']]';
return $block . $castext . '[[/pfs]]';
}
return $castext;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment