forked from maths/moodle-qtype_stack
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vle_specific.php
318 lines (301 loc) · 12.4 KB
/
vle_specific.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
<?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/>.
defined('MOODLE_INTERNAL') || die();
global $CFG;
// This file defines question_display_options which the next class extends.
require_once($CFG->libdir . '/questionlib.php');
require_once('questiondisplayoptions.php');
/**
* 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_cap('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_cap('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 'questiondescription':
case 'questionnote':
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;
}
/*
* This function returns the version number of the current Moodle.
*/
function stack_determine_moodle_version() {
$v = get_config('moodle');
return($v->branch);
}
/*
* This function returns fully defined URL for a file present in
* the `corsscripts` directory. Either mapped through logic that
* modifies headers or a direct link.
*/
function stack_cors_link(string $filename): string {
if (get_config('qtype_stack', 'stackapi')) {
return '/cors.php?name=' . $filename;
} else {
return (new moodle_url(
'/question/type/stack/corsscripts/cors.php', ['name' => $filename]))->out(false);
}
}
/*
* Gets the URL used for MathJax, might be VLE local.
*/
function stack_get_mathjax_url(): string {
// TO-DO: figure out how to support VLE local with CORS.
return 'https://cdn.jsdelivr.net/npm/[email protected]/MathJax.js?config=TeX-AMS-MML_HTMLorMML';
}
/*
* Gets the url for MathJax 3.
*/
function stack_get_mathjax3_url() {
return 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
}
/*
* Give the VLE a chance to clear any question cache.
*/
function stack_clear_vle_question_cache(int $questionid) {
question_bank::notify_question_edited($questionid);
}
/*
* This is needed to put links to the STACK question dashboard into the question.
*/
function question_display_options() {
$options = new qtype_stack_question_display_options();
$options->readonly = true;
$options->flags = question_display_options::HIDDEN;
$options->suppressruntestslink = true;
return $options;
}
/*
* This uses whatever methods the VLE wants to use to fetch included urls
* for the inclusion methods and can do caching at the request level.
*
* The requirements are as follows:
* 1. Must not cache, over multiple requests, the inclusion must use
* remote version at the time of inclusion.
* 2. Supports inclusion from http(s)://, contrib(l):// and template(l)://
* URLs.
* 3. contrib:// is special shorthand for fetching a file from a particular
* GitHub side folder. If the "l" suffix is there then the file will be read
* from a matching local folder, if fetching from GitHub fails we do not
* automatically fall-back to the local version.
* 4. template:// is similalr but has a different folder.
*
* contrib:// is for CAS side stuff and template:// is for CASText side stuff.
*
* Returns the string content of the URL/file. If failign return false.
*/
function stack_fetch_included_content(string $url) {
static $cache = [];
$lc = trim(strtolower($url));
$good = false;
$islocalfile = false;
// Not actually passing the $error out now, it is here for documentation
// and possible future use.
$error = 'Not a fetchable URL type.';
$translated = $url;
if (strpos($url, '://') === false) {
$good = false;
return false;
}
$path = explode('://', $url, 2)[1];
if (strpos($lc, 'http://') === 0 || strpos($lc, 'https://') === 0) {
$good = true;
} else {
if (strpos($path, '..') !== false || strpos($path, '/') === 0 || strpos($path, '~') === 0) {
$error = 'Traversing the directory tree is forbidden.';
$good = false;
return false;
}
}
if (strpos($lc, 'contrib://') === 0 || strpos($lc, 'contribl://') === 0) {
$good = true;
if (strpos($lc, 'contrib://') === 0) {
$translated = 'https://raw.githubusercontent.com/maths/moodle-qtype_stack/' .
'master/stack/maxima/contrib/' . $path;
} else {
$islocalfile = true;
$translated = __DIR__ . '/stack/maxima/contrib/' . $path;
}
} else if (strpos($lc, 'template://') === 0 || strpos($lc, 'templatel://') === 0) {
$good = true;
if (strpos($lc, 'template://') === 0) {
$translated = 'https://raw.githubusercontent.com/maths/moodle-qtype_stack/' .
'master/stack/cas/castext2/template/' . $path;
} else {
$islocalfile = true;
$translated = __DIR__ . '/stack/cas/castext2/template/' . $path;
}
}
if ($good) {
if (!isset($cache[$translated])) {
// Feel free to apply any proxying here if you want.
// Just remember that $islocalfile might be true and you might do
// something else then.
if ($islocalfile) {
$cache[$translated] = file_get_contents($translated);
} else {
$translated = clean_param($translated, PARAM_URL);
$headers = get_headers($translated);
if (strpos($headers[0], '404') === false) {
$cache[$translated] = download_file_content($translated);
} else {
$cache[$translated] = false;
}
}
}
return $cache[$translated];
}
$cache[$translated] = false;
return false;
}
/**
* Fetches the current VLE UI language. Might not be the one that the question uses.
*/
function stack_get_system_language(): string {
return current_language();
}