Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * This file defines the question attempt step class, and a few related classes.
19
 *
20
 * @package    moodlecore
21
 * @subpackage questionengine
22
 * @copyright  2009 The Open University
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
 
30
/**
31
 * Stores one step in a {@see question_attempt}.
32
 *
33
 * The most important attributes of a step are the state, which is one of the
34
 * {@see question_state} constants, the fraction, which may be null, or a
35
 * number bewteen the attempt's minfraction and maxfraction, and the array of submitted
36
 * data, about which more later.
37
 *
38
 * A step also tracks the time it was created, and the user responsible for
39
 * creating it.
40
 *
41
 * The submitted data is basically just an array of name => value pairs, with
42
 * certain conventions about the to divide the variables into five = 2 x 2 + 1 categories.
43
 *
44
 * Variables may either belong to the behaviour, in which case the
45
 * name starts with a '-', or they may belong to the question type in which case
46
 * they name does not start with a '-'.
47
 *
48
 * Second, variables may either be ones that came form the original request, in
49
 * which case the name does not start with an _, or they are cached values that
50
 * were created during processing, in which case the name does start with an _.
51
 *
52
 * In addition, we can store 'metadata', typically only in the first step of a
53
 * question attempt. These are stored with the initial characters ':_'.
54
 *
55
 * That is, each name will start with one of '', '_', '-', '-_' or ':_'. The remainder
56
 * of the name was supposed to match the regex [a-z][a-z0-9]* - but this has never
57
 * been enforced. Question types exist which break this rule. E.g. qtype_combined.
58
 * Perhpas now, an accurate regex would be [a-z][a-z0-9_:]*.
59
 *
60
 * These variables can be accessed with {@see get_behaviour_var()} and {@see get_qt_var()},
61
 * - to be clear, ->get_behaviour_var('x') gets the variable with name '-x' -
62
 * and values whose names start with '_' can be set using {@see set_behaviour_var()}
63
 * and {@see set_qt_var()}. There are some other methods like {@see has_behaviour_var()}
64
 * to check wether a varaible with a particular name is set, and {@see get_behaviour_data()}
65
 * to get all the behaviour data as an associative array. There are also
66
 * {@see get_metadata_var()}, {@see set_metadata_var()} and {@see has_metadata_var()},
67
 *
68
 * @copyright  2009 The Open University
69
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
70
 */
71
class question_attempt_step {
72
    /**
73
     * @var integer if this attempts is stored in the question_attempts table,
74
     * the id of that row.
75
     */
76
    private $id = null;
77
 
78
    /**
79
     * @var question_state one of the {@see question_state} constants.
80
     * The state after this step.
81
     */
82
    private $state;
83
 
84
    /**
85
     * @var null|number the fraction (grade on a scale of
86
     * minfraction .. maxfraction, normally 0..1) or null.
87
     */
88
    private $fraction = null;
89
 
90
    /** @var int the timestamp when this step was created. */
91
    private $timecreated;
92
 
93
    /** @var int the id of the user responsible for creating this step. */
94
    private $userid;
95
 
96
    /** @var array name => value pairs. The submitted data. */
97
    private $data;
98
 
99
    /** @var array name => array of {@see stored_file}s. Caches the contents of file areas. */
100
    private $files = array();
101
 
102
    /** @var stdClass|null User information. */
103
    private $user = null;
104
 
105
    /**
106
     * You should not need to call this constructor in your own code. Steps are
107
     * normally created by {@see question_attempt} methods like
108
     * {@see question_attempt::process_action()}.
109
     * @param array $data the submitted data that defines this step.
110
     * @param int|null $timecreated the time to record for the action. (If not given, use now.)
111
     * @param int|null $userid the user to attribute the aciton to. (If not given, use the current user.)
112
     * @param int|null $existingstepid if this step is going to replace an existing step
113
     *      (for example, during a regrade) this is the id of the previous step we are replacing.
114
     */
115
    public function __construct($data = [], $timecreated = null, $userid = null,
116
            $existingstepid = null) {
117
        global $USER;
118
 
119
        if (!is_array($data)) {
120
            throw new coding_exception('$data must be an array when constructing a question_attempt_step.');
121
        }
122
        $this->state = question_state::$unprocessed;
123
        $this->data = $data;
124
        if (is_null($timecreated)) {
125
            $this->timecreated = time();
126
        } else {
127
            $this->timecreated = $timecreated;
128
        }
129
        if (is_null($userid)) {
130
            $this->userid = $USER->id;
131
        } else {
132
            $this->userid = $userid;
133
        }
134
 
135
        if (!is_null($existingstepid)) {
136
            $this->id = $existingstepid;
137
        }
138
    }
139
 
140
    /**
141
     * @return int|null The id of this step in the database. null if this step
142
     * is not stored in the database.
143
     */
144
    public function get_id() {
145
        return $this->id;
146
    }
147
 
148
    /** @return question_state The state after this step. */
149
    public function get_state() {
150
        return $this->state;
151
    }
152
 
153
    /**
154
     * Set the state. Normally only called by behaviours.
155
     * @param question_state $state one of the {@see question_state} constants.
156
     */
157
    public function set_state($state) {
158
        $this->state = $state;
159
    }
160
 
161
    /**
162
     * @return null|number the fraction (grade on a scale of
163
     * minfraction .. maxfraction, normally 0..1),
164
     * or null if this step has not been marked.
165
     */
166
    public function get_fraction() {
167
        return $this->fraction;
168
    }
169
 
170
    /**
171
     * Set the fraction. Normally only called by behaviours.
172
     * @param null|number $fraction the fraction to set.
173
     */
174
    public function set_fraction($fraction) {
175
        $this->fraction = $fraction;
176
    }
177
 
178
    /** @return int the id of the user resonsible for creating this step. */
179
    public function get_user_id() {
180
        return $this->userid;
181
    }
182
 
183
    /**
184
     * Update full user information for step.
185
     *
186
     * @param stdClass $user Full user object.
187
     * @throws coding_exception
188
     */
189
    public function add_full_user_object(stdClass $user): void {
190
        if ($user->id != $this->userid) {
191
            throw new coding_exception('Wrong user passed to add_full_user_object');
192
        }
193
        $this->user = $user;
194
    }
195
 
196
    /**
197
     * Return the full user object.
198
     *
199
     * @return null|stdClass Get full user object.
200
     */
201
    public function get_user(): ?stdClass {
202
        if ($this->user === null) {
203
            debugging('Attempt to access the step user before it was initialised. ' .
204
                'Did you forget to call question_usage_by_activity::preload_all_step_users() or similar?', DEBUG_DEVELOPER);
205
        }
206
        return $this->user;
207
    }
208
 
209
    /**
210
     * Get full name of user who did action.
211
     *
212
     * @return string full name of user.
213
     */
214
    public function get_user_fullname(): string {
215
        return fullname($this->get_user());
216
    }
217
 
218
    /** @return int the timestamp when this step was created. */
219
    public function get_timecreated() {
220
        return $this->timecreated;
221
    }
222
 
223
    /**
224
     * @param string $name the name of a question type variable to look for in the submitted data.
225
     * @return bool whether a variable with this name exists in the question type data.
226
     */
227
    public function has_qt_var($name) {
228
        return array_key_exists($name, $this->data);
229
    }
230
 
231
    /**
232
     * @param string $name the name of a question type variable to look for in the submitted data.
233
     * @return string the requested variable, or null if the variable is not set.
234
     */
235
    public function get_qt_var($name) {
236
        if (!$this->has_qt_var($name)) {
237
            return null;
238
        }
239
        return $this->data[$name];
240
    }
241
 
242
    /**
243
     * Set a cached question type variable.
244
     * @param string $name the name of the variable to set. Must match _[a-z][a-z0-9]*.
245
     * @param string $value the value to set.
246
     */
247
    public function set_qt_var($name, $value) {
248
        if ($name[0] != '_') {
249
            throw new coding_exception('Cannot set question type data ' . $name .
250
                    ' on an attempt step. You can only set variables with names begining with _.');
251
        }
252
        $this->data[$name] = $value;
253
    }
254
 
255
    /**
256
     * Get the latest set of files for a particular question type variable of
257
     * type question_attempt::PARAM_FILES.
258
     *
259
     * @param string $name the name of the associated variable.
260
     * @param int $contextid contextid of the question attempt
261
     * @return array of {@see stored_files}.
262
     */
263
    public function get_qt_files($name, $contextid) {
264
        if (array_key_exists($name, $this->files)) {
265
            return $this->files[$name];
266
        }
267
 
268
        if (!$this->has_qt_var($name)) {
269
            $this->files[$name] = array();
270
            return array();
271
        }
272
 
273
        $fs = get_file_storage();
274
        $filearea = question_file_saver::clean_file_area_name('response_' . $name);
275
        $this->files[$name] = $fs->get_area_files($contextid, 'question',
276
                $filearea, $this->id, 'sortorder', false);
277
 
278
        return $this->files[$name];
279
    }
280
 
281
    /**
282
     * Prepare a draft file are for the files belonging the a response variable
283
     * of this step.
284
     *
285
     * @param string $name the variable name the files belong to.
286
     * @param int $contextid the id of the context the quba belongs to.
287
     * @return int the draft itemid.
288
     */
289
    public function prepare_response_files_draft_itemid($name, $contextid) {
290
        list($draftid, $notused) = $this->prepare_response_files_draft_itemid_with_text(
291
                $name, $contextid, null);
292
        return $draftid;
293
    }
294
 
295
    /**
296
     * Prepare a draft file are for the files belonging the a response variable
297
     * of this step, while rewriting the URLs in some text.
298
     *
299
     * @param string $name the variable name the files belong to.
300
     * @param int $contextid the id of the context the quba belongs to.
301
     * @param string|null $text the text to update the URLs in.
302
     * @return array(int, string) the draft itemid and the text with URLs rewritten.
303
     */
304
    public function prepare_response_files_draft_itemid_with_text($name, $contextid, $text) {
305
        $filearea = question_file_saver::clean_file_area_name('response_' . $name);
306
        $draftid = 0; // Will be filled in by file_prepare_draft_area.
307
        $newtext = file_prepare_draft_area($draftid, $contextid, 'question',
308
                $filearea, $this->id, null, $text);
309
        return array($draftid, $newtext);
310
    }
311
 
312
    /**
313
     * Rewrite the @@PLUGINFILE@@ tokens in a response variable from this step
314
     * that contains links to file. Normally you should probably call
315
     * {@see question_attempt::rewrite_response_pluginfile_urls()} instead of
316
     * calling this method directly.
317
     *
318
     * @param string $text the text to update the URLs in.
319
     * @param int $contextid the id of the context the quba belongs to.
320
     * @param string $name the variable name the files belong to.
321
     * @param array $extras extra file path components.
322
     * @return string the rewritten text.
323
     */
324
    public function rewrite_response_pluginfile_urls($text, $contextid, $name, $extras) {
325
        $filearea = question_file_saver::clean_file_area_name('response_' . $name);
326
        return question_rewrite_question_urls($text, 'pluginfile.php', $contextid,
327
                'question', $filearea, $extras, $this->id);
328
    }
329
 
330
    /**
331
     * Get all the question type variables.
332
     * @param array name => value pairs.
333
     */
334
    public function get_qt_data() {
335
        $result = array();
336
        foreach ($this->data as $name => $value) {
337
            if ($name[0] != '-' && $name[0] != ':') {
338
                $result[$name] = $value;
339
            }
340
        }
341
        return $result;
342
    }
343
 
344
    /**
345
     * @param string $name the name of a behaviour variable to look for in the submitted data.
346
     * @return bool whether a variable with this name exists in the question type data.
347
     */
348
    public function has_behaviour_var($name) {
349
        return array_key_exists('-' . $name, $this->data);
350
    }
351
 
352
    /**
353
     * @param string $name the name of a behaviour variable to look for in the submitted data.
354
     * @return string the requested variable, or null if the variable is not set.
355
     */
356
    public function get_behaviour_var($name) {
357
        if (!$this->has_behaviour_var($name)) {
358
            return null;
359
        }
360
        return $this->data['-' . $name];
361
    }
362
 
363
    /**
364
     * Set a cached behaviour variable.
365
     * @param string $name the name of the variable to set. Must match _[a-z][a-z0-9]*.
366
     * @param string $value the value to set.
367
     */
368
    public function set_behaviour_var($name, $value) {
369
        if ($name[0] != '_') {
370
            throw new coding_exception('Cannot set question type data ' . $name .
371
                    ' on an attempt step. You can only set variables with names begining with _.');
372
        }
373
        return $this->data['-' . $name] = $value;
374
    }
375
 
376
    /**
377
     * Get all the behaviour variables.
378
     *
379
     * @return array name => value pairs. NOTE! the name has the leading - stripped off.
380
     *      (If you don't understand the note, read the comment at the top of this class :-))
381
     */
382
    public function get_behaviour_data() {
383
        $result = array();
384
        foreach ($this->data as $name => $value) {
385
            if ($name[0] == '-') {
386
                $result[substr($name, 1)] = $value;
387
            }
388
        }
389
        return $result;
390
    }
391
 
392
    /**
393
     * Get all the submitted data, but not the cached data. behaviour
394
     * variables have the - at the start of their name. This is only really
395
     * intended for use by {@see question_attempt::regrade()}, it should not
396
     * be considered part of the public API.
397
     * @param array name => value pairs.
398
     */
399
    public function get_submitted_data() {
400
        $result = array();
401
        foreach ($this->data as $name => $value) {
402
            if ($name[0] == '_' || ($name[0] == '-' && $name[1] == '_')) {
403
                continue;
404
            }
405
            $result[$name] = $value;
406
        }
407
        return $result;
408
    }
409
 
410
    /**
411
     * Get all the data. behaviour variables have the - at the start of
412
     * their name. This is only intended for internal use, for example by
413
     * {@see question_engine_data_mapper::insert_question_attempt_step()},
414
     * however, it can occasionally be useful in test code. It should not be
415
     * considered part of the public API of this class.
416
     * @param array name => value pairs.
417
     */
418
    public function get_all_data() {
419
        return $this->data;
420
    }
421
 
422
    /**
423
     * Set a metadata variable.
424
     *
425
     * Do not call this method directly from  your code. It is for internal
426
     * use only. You should call {@see question_usage::set_question_attempt_metadata()}.
427
     *
428
     * @param string $name the name of the variable to set. [a-z][a-z0-9]*.
429
     * @param string $value the value to set.
430
     */
431
    public function set_metadata_var($name, $value) {
432
        $this->data[':_' . $name] = $value;
433
    }
434
 
435
    /**
436
     * Whether this step has a metadata variable.
437
     *
438
     * Do not call this method directly from  your code. It is for internal
439
     * use only. You should call {@see question_usage::get_question_attempt_metadata()}.
440
     *
441
     * @param string $name the name of the variable to set. [a-z][a-z0-9]*.
442
     * @return bool the value to set previously, or null if this variable was never set.
443
     */
444
    public function has_metadata_var($name) {
445
        return isset($this->data[':_' . $name]);
446
    }
447
 
448
    /**
449
     * Get a metadata variable.
450
     *
451
     * Do not call this method directly from  your code. It is for internal
452
     * use only. You should call {@see question_usage::get_question_attempt_metadata()}.
453
     *
454
     * @param string $name the name of the variable to set. [a-z][a-z0-9]*.
455
     * @return string the value to set previously, or null if this variable was never set.
456
     */
457
    public function get_metadata_var($name) {
458
        if (!$this->has_metadata_var($name)) {
459
            return null;
460
        }
461
        return $this->data[':_' . $name];
462
    }
463
 
464
    /**
465
     * Create a question_attempt_step from records loaded from the database.
466
     * @param Iterator $records Raw records loaded from the database.
467
     * @param int $stepid The id of the records to extract.
468
     * @param string $qtype The question type of which this is an attempt.
469
     *      If not given, each record must include a qtype field.
470
     * @return question_attempt_step The newly constructed question_attempt_step.
471
     */
472
    public static function load_from_records($records, $attemptstepid, $qtype = null) {
473
        $currentrec = $records->current();
474
        while ($currentrec->attemptstepid != $attemptstepid) {
475
            $records->next();
476
            if (!$records->valid()) {
477
                throw new coding_exception('Question attempt step ' . $attemptstepid .
478
                        ' not found in the database.');
479
            }
480
            $currentrec = $records->current();
481
        }
482
 
483
        $record = $currentrec;
484
        $contextid = null;
485
        $data = array();
486
        while ($currentrec && $currentrec->attemptstepid == $attemptstepid) {
487
            if (!is_null($currentrec->name)) {
488
                $data[$currentrec->name] = $currentrec->value;
489
            }
490
            $records->next();
491
            if ($records->valid()) {
492
                $currentrec = $records->current();
493
            } else {
494
                $currentrec = false;
495
            }
496
        }
497
 
498
        $step = new question_attempt_step_read_only($data, $record->timecreated, $record->userid);
499
        $step->state = question_state::get($record->state);
500
        $step->id = $record->attemptstepid;
501
        if (!is_null($record->fraction)) {
502
            $step->fraction = $record->fraction + 0;
503
        }
504
 
505
        // This next chunk of code requires getting $contextid and $qtype here.
506
        // Somehow, we need to get that information to this point by modifying
507
        // all the paths by which this method can be called.
508
        // Can we only return files when it's possible? Should there be some kind of warning?
509
        if (is_null($qtype)) {
510
            $qtype = $record->qtype;
511
        }
512
        foreach (question_bank::get_qtype($qtype)->response_file_areas() as $area) {
513
            if (empty($step->data[$area])) {
514
                continue;
515
            }
516
 
517
            $step->data[$area] = new question_file_loader($step, $area, $step->data[$area], $record->contextid);
518
        }
519
 
520
        return $step;
521
    }
522
}
523
 
524
 
525
/**
526
 * A subclass of {@see question_attempt_step} used when processing a new submission.
527
 *
528
 * When we are processing some new submitted data, which may or may not lead to
529
 * a new step being added to the {@see question_usage_by_activity} we create an
530
 * instance of this class. which is then passed to the question behaviour and question
531
 * type for processing. At the end of processing we then may, or may not, keep it.
532
 *
533
 * @copyright  2010 The Open University
534
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
535
 */
536
class question_attempt_pending_step extends question_attempt_step {
537
    /** @var string the new response summary, if there is one. */
538
    protected $newresponsesummary = null;
539
 
540
    /** @var int the new variant number, if there is one. */
541
    protected $newvariant = null;
542
 
543
    /**
544
     * If as a result of processing this step, the response summary for the
545
     * question attempt should changed, you should call this method to set the
546
     * new summary.
547
     * @param string $responsesummary the new response summary.
548
     */
549
    public function set_new_response_summary($responsesummary) {
550
        $this->newresponsesummary = $responsesummary;
551
    }
552
 
553
    /**
554
     * Get the new response summary, if there is one.
555
     * @return string the new response summary, or null if it has not changed.
556
     */
557
    public function get_new_response_summary() {
558
        return $this->newresponsesummary;
559
    }
560
 
561
    /**
562
     * Whether this processing this step has changed the response summary.
563
     * @return bool true if there is a new response summary.
564
     */
565
    public function response_summary_changed() {
566
        return !is_null($this->newresponsesummary);
567
    }
568
 
569
    /**
570
     * If as a result of processing this step, you identify that this variant of the
571
     * question is actually identical to the another one, you may change the
572
     * variant number recorded, in order to give better statistics. For an example
573
     * see qbehaviour_opaque.
574
     * @param int $variant the new variant number.
575
     */
576
    public function set_new_variant_number($variant) {
577
        $this->newvariant = $variant;
578
    }
579
 
580
    /**
581
     * Get the new variant number, if there is one.
582
     * @return int the new variant number, or null if it has not changed.
583
     */
584
    public function get_new_variant_number() {
585
        return $this->newvariant;
586
    }
587
 
588
    /**
589
     * Whether this processing this step has changed the variant number.
590
     * @return bool true if there is a new variant number.
591
     */
592
    public function variant_number_changed() {
593
        return !is_null($this->newvariant);
594
    }
595
}
596
 
597
 
598
/**
599
 * A subclass of {@see question_attempt_step} that cannot be modified.
600
 *
601
 * @copyright  2009 The Open University
602
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
603
 */
604
class question_attempt_step_read_only extends question_attempt_step {
605
    public function set_state($state) {
606
        throw new coding_exception('Cannot modify a question_attempt_step_read_only.');
607
    }
608
    public function set_fraction($fraction) {
609
        throw new coding_exception('Cannot modify a question_attempt_step_read_only.');
610
    }
611
    public function set_qt_var($name, $value) {
612
        throw new coding_exception('Cannot modify a question_attempt_step_read_only.');
613
    }
614
    public function set_behaviour_var($name, $value) {
615
        throw new coding_exception('Cannot modify a question_attempt_step_read_only.');
616
    }
617
}
618
 
619
 
620
/**
621
 * A null {@see question_attempt_step} returned from
622
 * {@see question_attempt::get_last_step()} etc. when a an attempt has just been
623
 * created and there is no actual step.
624
 *
625
 * @copyright  2009 The Open University
626
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
627
 */
628
class question_null_step {
629
    public function get_state() {
630
        return question_state::$notstarted;
631
    }
632
 
633
    public function set_state($state) {
634
        throw new coding_exception('This question has not been started.');
635
    }
636
 
637
    public function get_fraction() {
638
        return null;
639
    }
640
}
641
 
642
 
643
/**
644
 * This is an adapter class that wraps a {@see question_attempt_step} and
645
 * modifies the get/set_*_data methods so that they operate only on the parts
646
 * that belong to a particular subquestion, as indicated by an extra prefix.
647
 *
648
 * @copyright  2010 The Open University
649
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
650
 */
651
class question_attempt_step_subquestion_adapter extends question_attempt_step {
652
    /** @var question_attempt_step the step we are wrapping. */
653
    protected $realstep;
654
    /** @var string the exta prefix on fields we work with. */
655
    protected $extraprefix;
656
 
657
    /**
658
     * Constructor.
659
     * @param question_attempt_step $realstep the step to wrap. (Can be null if you
660
     *      just want to call add/remove.prefix.)
661
     * @param string $extraprefix the extra prefix that is used for date fields.
662
     */
663
    public function __construct($realstep, $extraprefix) {
664
        $this->realstep = $realstep;
665
        $this->extraprefix = $extraprefix;
666
    }
667
 
668
    /**
669
     * Add the extra prefix to a field name.
670
     * @param string $field the plain field name.
671
     * @return string the field name with the extra bit of prefix added.
672
     */
673
    public function add_prefix($field) {
674
        if (substr($field, 0, 2) === '-_') {
675
            return '-_' . $this->extraprefix . substr($field, 2);
676
        } else if (substr($field, 0, 1) === '-') {
677
            return '-' . $this->extraprefix . substr($field, 1);
678
        } else if (substr($field, 0, 1) === '_') {
679
            return '_' . $this->extraprefix . substr($field, 1);
680
        } else {
681
            return $this->extraprefix . $field;
682
        }
683
    }
684
 
685
    /**
686
     * Remove the extra prefix from a field name if it is present.
687
     * @param string $field the extended field name.
688
     * @return string the field name with the extra bit of prefix removed, or
689
     * null if the extre prefix was not present.
690
     */
691
    public function remove_prefix($field) {
692
        if (preg_match('~^(-?_?)' . preg_quote($this->extraprefix, '~') . '(.*)$~', $field, $matches)) {
693
            return $matches[1] . $matches[2];
694
        } else {
695
            return null;
696
        }
697
    }
698
 
699
    /**
700
     * Filter some data to keep only those entries where the key contains
701
     * extraprefix, and remove the extra prefix from the reutrned arrary.
702
     * @param array $data some of the data stored in this step.
703
     * @return array the data with the keys ajusted using {@see remove_prefix()}.
704
     */
705
    public function filter_array($data) {
706
        $result = array();
707
        foreach ($data as $fullname => $value) {
708
            if ($name = $this->remove_prefix($fullname)) {
709
                $result[$name] = $value;
710
            }
711
        }
712
        return $result;
713
    }
714
 
715
    public function get_state() {
716
        return $this->realstep->get_state();
717
    }
718
 
719
    public function set_state($state) {
720
        throw new coding_exception('Cannot modify a question_attempt_step_subquestion_adapter.');
721
    }
722
 
723
    public function get_fraction() {
724
        return $this->realstep->get_fraction();
725
    }
726
 
727
    public function set_fraction($fraction) {
728
        throw new coding_exception('Cannot modify a question_attempt_step_subquestion_adapter.');
729
    }
730
 
731
    public function get_user_id() {
732
        return $this->realstep->get_user_id();
733
    }
734
 
735
    public function get_timecreated() {
736
        return $this->realstep->get_timecreated();
737
    }
738
 
739
    public function has_qt_var($name) {
740
        return $this->realstep->has_qt_var($this->add_prefix($name));
741
    }
742
 
743
    public function get_qt_var($name) {
744
        return $this->realstep->get_qt_var($this->add_prefix($name));
745
    }
746
 
747
    public function set_qt_var($name, $value) {
748
        $this->realstep->set_qt_var($this->add_prefix($name), $value);
749
    }
750
 
751
    public function get_qt_data() {
752
        return $this->filter_array($this->realstep->get_qt_data());
753
    }
754
 
755
    public function has_behaviour_var($name) {
756
        return $this->realstep->has_behaviour_var($this->add_prefix($name));
757
    }
758
 
759
    public function get_behaviour_var($name) {
760
        return $this->realstep->get_behaviour_var($this->add_prefix($name));
761
    }
762
 
763
    public function set_behaviour_var($name, $value) {
764
        return $this->realstep->set_behaviour_var($this->add_prefix($name), $value);
765
    }
766
 
767
    public function get_behaviour_data() {
768
        return $this->filter_array($this->realstep->get_behaviour_data());
769
    }
770
 
771
    public function get_submitted_data() {
772
        return $this->filter_array($this->realstep->get_submitted_data());
773
    }
774
 
775
    public function get_all_data() {
776
        return $this->filter_array($this->realstep->get_all_data());
777
    }
778
 
779
    public function get_qt_files($name, $contextid) {
780
        throw new coding_exception('No attempt has yet been made to implement files support in ' .
781
                'question_attempt_step_subquestion_adapter.');
782
    }
783
 
784
    public function prepare_response_files_draft_itemid($name, $contextid) {
785
        throw new coding_exception('No attempt has yet been made to implement files support in ' .
786
                'question_attempt_step_subquestion_adapter.');
787
    }
788
 
789
    public function prepare_response_files_draft_itemid_with_text($name, $contextid, $text) {
790
        throw new coding_exception('No attempt has yet been made to implement files support in ' .
791
                'question_attempt_step_subquestion_adapter.');
792
    }
793
 
794
    public function rewrite_response_pluginfile_urls($text, $contextid, $name, $extras) {
795
        throw new coding_exception('No attempt has yet been made to implement files support in ' .
796
                'question_attempt_step_subquestion_adapter.');
797
    }
798
}