Skip to content

Commit 8f0bd58

Browse files
committed
wip
1 parent a0b4b8d commit 8f0bd58

File tree

3 files changed

+226
-59
lines changed

3 files changed

+226
-59
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
// This file is part of the QuestionPy Moodle plugin - https://questionpy.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+
namespace qtype_questionpy\local\attempt_ui;
18+
19+
20+
use core\exception\coding_exception;
21+
use DOMElement;
22+
use DOMNode;
23+
use file_exception;
24+
use moodle_exception;
25+
use question_attempt;
26+
use stored_file_creation_exception;
27+
28+
/**
29+
* Represents a `<qpy:X/>` element in the question UI XML.
30+
*
31+
* @package qtype_questionpy
32+
* @author Maximilian Haye
33+
* @copyright 2025 TU Berlin, innoCampus {@link https://www.questionpy.org}
34+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35+
*/
36+
interface custom_xhtml_element {
37+
/**
38+
* Parses the given DOMElement if possible.
39+
*
40+
* @param DOMElement $element
41+
* @return static|null
42+
*/
43+
public static function from_element(DOMElement $element): ?static;
44+
45+
/**
46+
* Renders this element to a DOMNode.
47+
*
48+
* @param question_attempt $qa
49+
* @param question_ui_renderer $renderer
50+
* @return DOMNode
51+
* @throws coding_exception
52+
* @throws file_exception
53+
* @throws moodle_exception
54+
* @throws stored_file_creation_exception
55+
*/
56+
public function render(question_attempt $qa, question_ui_renderer $renderer): DOMNode;
57+
}

classes/local/attempt_ui/qpy_file_upload.php

Lines changed: 6 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
* @copyright 2025 TU Berlin, innoCampus {@link https://www.questionpy.org}
4040
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4141
*/
42-
class qpy_file_upload {
42+
class qpy_file_upload implements custom_xhtml_element {
4343
/**
4444
* Trivial private constructor. Use {@see from_element()}.
4545
* @param DOMElement $element
@@ -81,12 +81,12 @@ public function get_limits_in(context $context): validatable_upload_limits {
8181
}
8282

8383
/**
84-
* Creates a new {@see qpy_file_upload} from a given {@see DOMElement}.
84+
* Parses the given DOMElement if possible.
8585
*
8686
* @param DOMElement $element
87-
* @return self|null
87+
* @return static|null
8888
*/
89-
public static function from_element(DOMElement $element): ?self {
89+
public static function from_element(DOMElement $element): ?static {
9090
$name = $element->getAttribute('name');
9191
if (!$name) {
9292
debugging('qpy:file-upload without a name');
@@ -102,62 +102,9 @@ public static function from_element(DOMElement $element): ?self {
102102
* @param question_attempt $qa
103103
* @param question_ui_renderer $renderer
104104
* @return DOMNode
105-
* @throws coding_exception
106-
* @throws file_exception
107-
* @throws moodle_exception
108-
* @throws stored_file_creation_exception
109105
*/
110106
public function render(question_attempt $qa, question_ui_renderer $renderer): DOMNode {
111-
if ($renderer->options->readonly) {
112-
global $PAGE;
113-
/** @var qtype_questionpy_renderer $qpyrenderer */
114-
$qpyrenderer = $PAGE->get_renderer('qtype_questionpy');
115-
$html = $qpyrenderer->render_readonly_file_view($qa, $this->name, $renderer->options);
116-
return dom_utils::html_to_fragment($this->element->ownerDocument, $html);
117-
} else {
118-
return $this->render_writable($qa, $renderer);
119-
}
120-
}
121-
122-
/**
123-
* Renders a Moodle file manager from this `<qpy:file-upload/>`, preparing it with the last submitted files.
124-
*
125-
* @param question_attempt $qa
126-
* @param question_ui_renderer $renderer
127-
* @return DOMNode
128-
* @throws coding_exception
129-
* @throws file_exception
130-
* @throws moodle_exception
131-
* @throws stored_file_creation_exception
132-
*/
133-
private function render_writable(question_attempt $qa, question_ui_renderer $renderer): DOMNode {
134-
// Re: "global $PAGE cannot be used in renderers" - We're not _that_ kind of a renderer.
135-
// phpcs:disable moodle.PHP.ForbiddenGlobalUse.BadGlobal
136-
global $CFG, $PAGE, $USER;
137-
require_once($CFG->libdir . '/form/filemanager.php');
138-
139-
$combineddraftitemid = $renderer->prepare_combined_draft_area($qa);
140-
141-
$rfs = di::get(response_file_service::class);
142-
$splitdraftitemid = $rfs->prepare_split_draft_area($this->name, $USER->id, $combineddraftitemid);
143-
144-
// This is used to tell the qbehaviour what draft areas to save.
145-
$renderer->draftareas[$this->name] = $splitdraftitemid;
146-
147-
$limits = $this->get_limits_in($renderer->options->context);
148-
149-
$fm = new form_filemanager((object)[
150-
'itemid' => $splitdraftitemid,
151-
'subdirs' => false,
152-
'context' => $renderer->options->context,
153-
'maxfiles' => $limits->maxfiles,
154-
'maxbytes' => $limits->maxbytes,
155-
'areamaxbytes' => $limits->areamaxbytes,
156-
]);
157-
158-
// phpcs:disable moodle.PHP.ForbiddenGlobalUse.BadGlobal
159-
$filesrenderer = $PAGE->get_renderer('core', 'files');
160-
$html = $filesrenderer->render($fm);
161-
return dom_utils::html_to_fragment($this->element->ownerDocument, $html);
107+
// TODO: do.
108+
return new \DOMText();
162109
}
163110
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
// This file is part of the QuestionPy Moodle plugin - https://questionpy.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+
namespace qtype_questionpy\local\attempt_ui;
18+
19+
use core\context;
20+
use core\di;
21+
use core\exception\coding_exception;
22+
use DOMDocument;
23+
use DOMElement;
24+
use DOMNode;
25+
use file_exception;
26+
use form_filemanager;
27+
use moodle_exception;
28+
use qtype_questionpy\local\files\response_file_service;
29+
use qtype_questionpy\local\files\validatable_upload_limits;
30+
use qtype_questionpy_renderer;
31+
use question_attempt;
32+
use stored_file_creation_exception;
33+
34+
/**
35+
* Represents a `<qpy:rich-text-editor/>` element in the question UI XML.
36+
*
37+
* @package qtype_questionpy
38+
* @author Maximilian Haye
39+
* @copyright 2025 TU Berlin, innoCampus {@link https://www.questionpy.org}
40+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41+
*/
42+
class qpy_rich_text_editor implements custom_xhtml_element {
43+
/**
44+
* Trivial private constructor. Use {@see from_element()}.
45+
* @param DOMElement $element
46+
* @param string $name
47+
*/
48+
private function __construct(
49+
/** @var DOMElement */
50+
private readonly DOMElement $element,
51+
/** @var string */
52+
public readonly string $name,
53+
) {
54+
}
55+
56+
/**
57+
* Gets the limits that should be validated for the current user in the given context when using this upload field.
58+
*
59+
* @param context $context
60+
* @return validatable_upload_limits
61+
*/
62+
public function get_limits_in(context $context): validatable_upload_limits {
63+
global $CFG, $PAGE;
64+
require_once($CFG->libdir . '/formslib.php'); // For EDITOR_UNLIMITED_FILES.
65+
66+
$maxfiles = $this->element->getAttribute('max-files');
67+
$maxfiles = is_numeric($maxfiles) ? intval($maxfiles) : EDITOR_UNLIMITED_FILES;
68+
69+
$maxbytes = $this->element->getAttribute('max-bytes-per-file');
70+
$maxbytes = is_numeric($maxbytes) ? intval($maxbytes) : FILE_AREA_MAX_BYTES_UNLIMITED;
71+
$coursemaxbytes = 0;
72+
if (!empty($PAGE->course->maxbytes)) {
73+
$coursemaxbytes = $PAGE->course->maxbytes;
74+
}
75+
$maxbytes = get_user_max_upload_file_size($context, $CFG->maxbytes, $coursemaxbytes, $maxbytes);
76+
77+
$areamaxbytes = $this->element->getAttribute('max-bytes-total');
78+
$areamaxbytes = is_numeric($areamaxbytes) ? intval($areamaxbytes) : FILE_AREA_MAX_BYTES_UNLIMITED;
79+
80+
return new validatable_upload_limits($maxfiles, $maxbytes, $areamaxbytes);
81+
}
82+
83+
/**
84+
* Parses the given DOMElement if possible.
85+
*
86+
* @param DOMElement $element
87+
* @return static|null
88+
*/
89+
public static function from_element(DOMElement $element): ?static {
90+
$name = $element->getAttribute('name');
91+
if (!$name) {
92+
debugging('qpy:file-upload without a name');
93+
return null;
94+
}
95+
96+
return new static($element, $name);
97+
}
98+
99+
/**
100+
* Renders this element to a DOMNode.
101+
*
102+
* @param question_attempt $qa
103+
* @param question_ui_renderer $renderer
104+
* @return DOMNode
105+
* @throws coding_exception
106+
* @throws file_exception
107+
* @throws moodle_exception
108+
* @throws stored_file_creation_exception
109+
*/
110+
public function render(question_attempt $qa, question_ui_renderer $renderer): DOMNode {
111+
if ($renderer->options->readonly) {
112+
global $PAGE;
113+
/** @var qtype_questionpy_renderer $qpyrenderer */
114+
$qpyrenderer = $PAGE->get_renderer('qtype_questionpy');
115+
$html = $qpyrenderer->render_readonly_file_view($qa, $this->name, $renderer->options);
116+
return dom_utils::html_to_fragment($this->element->ownerDocument, $html);
117+
} else {
118+
return $this->render_writable($qa, $renderer);
119+
}
120+
}
121+
122+
/**
123+
* Renders a Moodle file manager from this `<qpy:file-upload/>`, preparing it with the last submitted files.
124+
*
125+
* @param question_attempt $qa
126+
* @param question_ui_renderer $renderer
127+
* @return DOMNode
128+
* @throws coding_exception
129+
* @throws file_exception
130+
* @throws moodle_exception
131+
* @throws stored_file_creation_exception
132+
*/
133+
private function render_writable(question_attempt $qa, question_ui_renderer $renderer): DOMNode {
134+
// Re: "global $PAGE cannot be used in renderers" - We're not _that_ kind of a renderer.
135+
// phpcs:disable moodle.PHP.ForbiddenGlobalUse.BadGlobal
136+
global $CFG, $PAGE, $USER;
137+
require_once($CFG->libdir . '/form/filemanager.php');
138+
139+
$combineddraftitemid = $renderer->prepare_combined_draft_area($qa);
140+
141+
$rfs = di::get(response_file_service::class);
142+
$splitdraftitemid = $rfs->prepare_split_draft_area($this->name, $USER->id, $combineddraftitemid);
143+
144+
// This is used to tell the qbehaviour what draft areas to save.
145+
$renderer->draftareas[$this->name] = $splitdraftitemid;
146+
147+
$limits = $this->get_limits_in($renderer->options->context);
148+
149+
$fm = new form_filemanager((object)[
150+
'itemid' => $splitdraftitemid,
151+
'subdirs' => false,
152+
'context' => $renderer->options->context,
153+
'maxfiles' => $limits->maxfiles,
154+
'maxbytes' => $limits->maxbytes,
155+
'areamaxbytes' => $limits->areamaxbytes,
156+
]);
157+
158+
// phpcs:disable moodle.PHP.ForbiddenGlobalUse.BadGlobal
159+
$filesrenderer = $PAGE->get_renderer('core', 'files');
160+
$html = $filesrenderer->render($fm);
161+
return dom_utils::html_to_fragment($this->element->ownerDocument, $html);
162+
}
163+
}

0 commit comments

Comments
 (0)