Skip to content

Commit c27354d

Browse files
authored
feat: form rendering
1 parent 258fc22 commit c27354d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2246
-7
lines changed

.github/workflows/moodle-ci.yml

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
name: Moodle Plugin CI
22

3-
on: [push, pull_request]
3+
on:
4+
push:
5+
pull_request:
6+
# We don't run on PR synchronize, because that would duplicate the push-Build
7+
types:
8+
- opened
49

510
jobs:
611
test:
712
runs-on: ubuntu-20.04
813

914
services:
1015
postgres:
11-
image: postgres:10
16+
image: postgres:12
1217
env:
1318
POSTGRES_USER: 'postgres'
1419
POSTGRES_HOST_AUTH_METHOD: 'trust'
@@ -30,8 +35,8 @@ jobs:
3035
strategy:
3136
fail-fast: false
3237
matrix:
33-
php: ['7.3', '7.4', '8.0']
34-
moodle-branch: ['MOODLE_400_STABLE']
38+
php: ['7.4', '8.0']
39+
moodle-branch: ['MOODLE_400_STABLE', 'master']
3540
database: [pgsql, mariadb]
3641

3742
steps:

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/ci
+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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\form\elements;
18+
19+
use qtype_questionpy\form\render_context;
20+
21+
/**
22+
* Element displaying a labelled checkbox.
23+
*
24+
* @package qtype_questionpy
25+
* @author Maximilian Haye
26+
* @copyright 2022 TU Berlin, innoCampus {@link https://www.questionpy.org}
27+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28+
*/
29+
class checkbox_element extends form_element {
30+
/** @var string */
31+
public string $name;
32+
/** @var string|null */
33+
public ?string $leftlabel = null;
34+
/** @var string|null */
35+
public ?string $rightlabel = null;
36+
/** @var bool */
37+
public bool $required = false;
38+
/** @var bool */
39+
public bool $selected = false;
40+
41+
/**
42+
* Initializes the element.
43+
*
44+
* @param string $name
45+
* @param string|null $leftlabel
46+
* @param string|null $rightlabel
47+
* @param bool $required
48+
* @param bool $selected
49+
*/
50+
public function __construct(string $name, ?string $leftlabel = null, ?string $rightlabel = null,
51+
bool $required = false, bool $selected = false) {
52+
$this->name = $name;
53+
$this->leftlabel = $leftlabel;
54+
$this->rightlabel = $rightlabel;
55+
$this->required = $required;
56+
$this->selected = $selected;
57+
}
58+
59+
/**
60+
* Convert the given array to the concrete element without checking the `kind` descriptor.
61+
* (Which is done by {@see from_array_any}.)
62+
*
63+
* @param array $array source array, probably parsed from JSON
64+
*/
65+
public static function from_array(array $array): self {
66+
return new self(
67+
$array["name"],
68+
$array["left_label"] ?? null,
69+
$array["right_label"] ?? null,
70+
$array["required"] ?? false,
71+
$array["selected"] ?? false,
72+
);
73+
}
74+
75+
/**
76+
* Convert this element except for the `kind` descriptor to an array suitable for json encoding.
77+
* The default implementation just casts to an array, which is suitable only if the json field names match the
78+
* class property names.
79+
*/
80+
public function to_array(): array {
81+
return [
82+
"name" => $this->name,
83+
"left_label" => $this->leftlabel,
84+
"right_label" => $this->rightlabel,
85+
"required" => $this->required,
86+
"selected" => $this->selected,
87+
];
88+
}
89+
90+
/**
91+
* The `kind` field of an element's JSON representation serves as a descriptor field. {@see from_array_any()} uses
92+
* it to determine the concrete class to use for deserialization.
93+
*
94+
* @return string the value of this element's `kind` field.
95+
*/
96+
protected static function kind(): string {
97+
return "checkbox";
98+
}
99+
100+
/**
101+
* Render this item to the given context.
102+
*
103+
* @param render_context $context target context
104+
* @param int|null $group passed by {@see checkbox_group_element::render_to} to the checkboxes belonging to
105+
* it
106+
*/
107+
public function render_to(render_context $context, ?int $group = null): void {
108+
$context->add_element(
109+
"advcheckbox", $this->name, $this->leftlabel, $this->rightlabel,
110+
$group ? ["group" => $group] : null
111+
);
112+
113+
if ($this->selected) {
114+
$context->set_default($this->name, "1");
115+
}
116+
if ($this->required) {
117+
$context->add_rule($this->name, get_string("required"), "required");
118+
}
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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\form\elements;
18+
19+
use qtype_questionpy\form\render_context;
20+
21+
/**
22+
* Element grouping one or more checkboxes with a `Select all/none` button.
23+
*
24+
* @package qtype_questionpy
25+
* @author Maximilian Haye
26+
* @copyright 2022 TU Berlin, innoCampus {@link https://www.questionpy.org}
27+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28+
*/
29+
class checkbox_group_element extends form_element {
30+
/** @var checkbox_element[] */
31+
public array $checkboxes = [];
32+
33+
/**
34+
* Initializes the element.
35+
*
36+
* @param checkbox_element ...$checkboxes
37+
*/
38+
public function __construct(checkbox_element...$checkboxes) {
39+
$this->checkboxes = $checkboxes;
40+
}
41+
42+
/**
43+
* The `kind` field of an element's JSON representation serves as a descriptor field. {@see from_array_any()} uses
44+
* it to determine the concrete class to use for deserialization.
45+
*
46+
* @return string the value of this element's `kind` field.
47+
*/
48+
protected static function kind(): string {
49+
return "checkbox_group";
50+
}
51+
52+
/**
53+
* Render this item to the given context.
54+
*
55+
* @param render_context $context target context
56+
* @package qtype_questionpy
57+
*/
58+
public function render_to(render_context $context): void {
59+
$groupid = $context->next_unique_int();
60+
61+
foreach ($this->checkboxes as $checkbox) {
62+
$checkbox->render_to($context, $groupid);
63+
}
64+
65+
$context->add_checkbox_controller($groupid);
66+
}
67+
68+
/**
69+
* Convert the given array to the concrete element without checking the `kind` descriptor.
70+
* (Which is done by {@see from_array_any}.)
71+
*
72+
* @param array $array source array, probably parsed from JSON
73+
*/
74+
public static function from_array(array $array): self {
75+
return new self(...array_map([checkbox_element::class, "from_array"], $array["checkboxes"]));
76+
}
77+
}
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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\form\elements;
18+
19+
use qtype_questionpy\form\renderable;
20+
21+
/**
22+
* Base class for QuestionPy form elements.
23+
*
24+
* @package qtype_questionpy
25+
* @author Maximilian Haye
26+
* @copyright 2022 TU Berlin, innoCampus {@link https://www.questionpy.org}
27+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28+
*/
29+
abstract class form_element implements renderable, \JsonSerializable {
30+
/**
31+
* @var string[] class names of all available concrete elements, for deserialization
32+
*/
33+
private static array $elementclasses = [
34+
checkbox_element::class,
35+
checkbox_group_element::class,
36+
group_element::class,
37+
hidden_element::class,
38+
radio_group_element::class,
39+
select_element::class,
40+
static_text_element::class,
41+
text_input_element::class,
42+
];
43+
44+
/**
45+
* The `kind` field of an element's JSON representation serves as a descriptor field. {@see from_array_any()} uses
46+
* it to determine the concrete class to use for deserialization.
47+
*
48+
* @return string the value of this element's `kind` field.
49+
*/
50+
abstract protected static function kind(): string;
51+
52+
/**
53+
* Convert the given array to the concrete element without checking the `kind` descriptor.
54+
* (Which is done by {@see from_array_any}.)
55+
*
56+
* @param array $array source array, probably parsed from JSON
57+
*/
58+
abstract public static function from_array(array $array): self;
59+
60+
/**
61+
* Convert this element except for the `kind` descriptor to an array suitable for json encoding.
62+
* The default implementation just casts to an array, which is suitable only if the json field names match the
63+
* class property names.
64+
*/
65+
public function to_array(): array {
66+
return (array)$this;
67+
}
68+
69+
/**
70+
* Use the value of the `kind` descriptor to convert the given array to the correct concrete element,
71+
* delegating to the appropriate {@see from_array} implementation.
72+
*
73+
* @param array $array source array, probably parsed from JSON
74+
*/
75+
final public static function from_array_any(array $array): self {
76+
$kind = $array["kind"];
77+
foreach (self::$elementclasses as $elementclass) {
78+
if ($elementclass::kind() == $kind) {
79+
return $elementclass::from_array($array);
80+
}
81+
}
82+
throw new \RuntimeException("Unknown form element kind: " . $kind);
83+
}
84+
85+
/**
86+
* Serializes this element by calling {@see to_array} and adding its {@see kind} to the result.
87+
*/
88+
public function jsonSerialize(): array {
89+
return array_merge(
90+
["kind" => $this->kind()],
91+
$this->to_array()
92+
);
93+
}
94+
}

0 commit comments

Comments
 (0)