Skip to content

Commit

Permalink
feat: add generated_id_element
Browse files Browse the repository at this point in the history
  • Loading branch information
MHajoha committed Dec 19, 2024
1 parent dc39584 commit 18bb7da
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 18 deletions.
41 changes: 25 additions & 16 deletions classes/form/context/array_render_context.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class array_render_context extends render_context {
* Initializes a new array-based context.
*
* @param render_context $parent context containing this group
* @param string $prefix prefix for the names of elements in this context
* @param string $prefix prefix for the names of elements in this context
*/
public function __construct(render_context $parent, string $prefix) {
$this->parent = $parent;
Expand All @@ -82,8 +82,8 @@ public function __construct(render_context $parent, string $prefix) {
/**
* Create, add and return an element.
*
* @param string $type the type name of the element, as per the Moodle docs.
* @param string $name the name of the generated form element.
* @param string $type the type name of the element, as per the Moodle docs.
* @param string $name the name of the generated form element.
* @param mixed ...$args remaining arguments specific to the element type.
* @return object the created element. Really an instance of {@see \HTML_QuickForm_element}, but the return type of
* {@see \MoodleQuickForm::addElement()} is also an object.
Expand All @@ -109,7 +109,7 @@ public function set_type(string $name, string $type): void {
/**
* Sets the default of an element which has been (or will be) added independently.
*
* @param string $name the name of the target element.
* @param string $name the name of the target element.
* @param mixed $default default value for the element.
* @see \MoodleQuickForm::setDefault()
*/
Expand All @@ -122,14 +122,14 @@ public function set_default(string $name, $default): void {
*
* Must be called *after* the element was added using {@see add_element}.
*
* @param string $name the name of the target element.
* @param string|null $message message to display for invalid data.
* @param string $type rule type, use getRegisteredRules() to get types.
* @param string|null $format required for extra rule data.
* @param string $name the name of the target element.
* @param string|null $message message to display for invalid data.
* @param string $type rule type, use getRegisteredRules() to get types.
* @param string|null $format required for extra rule data.
* @param string|null $validation where to perform validation: "server", "client".
* @param bool $reset client-side validation: reset the form element to its original value if there is
* @param bool $reset client-side validation: reset the form element to its original value if there is
* an error?
* @param bool $force force the rule to be applied, even if the target form element does not exist.
* @param bool $force force the rule to be applied, even if the target form element does not exist.
* @see \MoodleQuickForm::addRule()
*/
public function add_rule(string $name, ?string $message, string $type, ?string $format = null,
Expand All @@ -144,9 +144,9 @@ public function add_rule(string $name, ?string $message, string $type, ?string $
* Adds a condition which will disable the named element if met.
*
* @param string $dependant name of the element which has the dependency on another element
* @param string $dependency absolute name of the element which is depended on
* @param string $operator one of a fixed set of conditions, as in {@see MoodleQuickForm::disabledIf}
* @param mixed $value for conditions requiring it, the value to compare with. Ignored otherwise.
* @param string $dependency absolute name of the element which is depended on
* @param string $operator one of a fixed set of conditions, as in {@see MoodleQuickForm::disabledIf}
* @param mixed $value for conditions requiring it, the value to compare with. Ignored otherwise.
* @see \MoodleQuickForm::disabledIf()
*/
public function disable_if(string $dependant, string $dependency, string $operator, $value = null): void {
Expand All @@ -157,9 +157,9 @@ public function disable_if(string $dependant, string $dependency, string $operat
* Adds a condition which will hide the named element if met.
*
* @param string $dependant name of the element which has the dependency on another element
* @param string $dependency absolute name of the element which is depended on
* @param string $operator one of a fixed set of conditions, as in {@see MoodleQuickForm::hideIf}
* @param mixed $value for conditions requiring it, the value to compare with. Ignored otherwise.
* @param string $dependency absolute name of the element which is depended on
* @param string $operator one of a fixed set of conditions, as in {@see MoodleQuickForm::hideIf}
* @param mixed $value for conditions requiring it, the value to compare with. Ignored otherwise.
* @see \MoodleQuickForm::hideIf()
*/
public function hide_if(string $dependant, string $dependency, string $operator, $value = null): void {
Expand Down Expand Up @@ -187,4 +187,13 @@ public function next_unique_int(): int {
public function contextualize(?string $text): ?string {
return $this->parent->contextualize($text);
}

/**
* Generate a new UUID. Probably uses {@see uuid}, but may be overridden for tests.
*
* @return string
*/
public function generate_uuid(): string {
return $this->parent->generate_uuid();
}
}
1 change: 1 addition & 0 deletions classes/form/context/mform_render_context.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

namespace qtype_questionpy\form\context;

use Closure;
use qtype_questionpy\utils;

/**
Expand Down
8 changes: 8 additions & 0 deletions classes/form/context/render_context.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

namespace qtype_questionpy\form\context;

use core\uuid;
use moodleform;
use MoodleQuickForm;
use qtype_questionpy\utils;
Expand Down Expand Up @@ -212,4 +213,11 @@ public function reference_to_absolute(string $reference): string {
* @return string|null input string with format specifiers replaced
*/
abstract public function contextualize(?string $text): ?string;

/**
* Generate a new UUID. Probably uses {@see uuid}, but may be overridden for tests.
*
* @return string
*/
abstract public function generate_uuid(): string;
}
14 changes: 14 additions & 0 deletions classes/form/context/root_render_context.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

namespace qtype_questionpy\form\context;

use core\uuid;

/**
* Uppermost render context.
*
Expand All @@ -28,6 +30,9 @@ class root_render_context extends mform_render_context {
/** @var int the next int which will be returned by {@see next_unique_int} */
private int $nextuniqueint = 1;

/** @var callable can be set from tests to supply mock UUIDs */
public $uuidgen = [uuid::class, 'generate'];

/**
* Get a unique and deterministic integer for use in generated element names and IDs.
*
Expand All @@ -48,4 +53,13 @@ public function next_unique_int(): int {
public function contextualize(?string $text): ?string {
return $text;
}

/**
* Generate a new UUID. Probably uses {@see uuid}, but may be overridden for tests.
*
* @return string
*/
public function generate_uuid(): string {
return ($this->uuidgen)();
}
}
11 changes: 10 additions & 1 deletion classes/form/context/section_render_context.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class section_render_context extends mform_render_context {
* Initializes a new {@see section_render_context}.
*
* @param render_context $parent context containing this section
* @param string $name the name part which will be appended to `$parent`'s prefix
* @param string $name the name part which will be appended to `$parent`'s prefix
*/
public function __construct(render_context $parent, string $name) {
$this->parent = $parent;
Expand Down Expand Up @@ -69,4 +69,13 @@ public function next_unique_int(): int {
public function contextualize(?string $text): ?string {
return $this->parent->contextualize($text);
}

/**
* Generate a new UUID. Probably uses {@see uuid}, but may be overridden for tests.
*
* @return string
*/
public function generate_uuid(): string {
return $this->parent->generate_uuid();
}
}
3 changes: 2 additions & 1 deletion classes/form/elements/form_element.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* @copyright 2022 TU Berlin, innoCampus {@link https://www.questionpy.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[array_polymorphic('kind', variants: [
#[array_polymorphic(discriminator: 'kind', variants: [
'checkbox' => checkbox_element::class,
'checkbox_group' => checkbox_group_element::class,
'group' => group_element::class,
Expand All @@ -38,6 +38,7 @@
'static_text' => static_text_element::class,
'input' => text_input_element::class,
'textarea' => text_area_element::class,
'id' => generated_id_element::class,
], fallbackvariant: fallback_element::class)]
abstract class form_element implements qpy_renderable {
}
60 changes: 60 additions & 0 deletions classes/form/elements/generated_id_element.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
// This file is part of the QuestionPy Moodle plugin - https://questionpy.org
//
// Moodle 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.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace qtype_questionpy\form\elements;

use core\uuid;
use qtype_questionpy\form\context\render_context;


/**
* Generates a unique ID which won't change across form saves.
*
* @package qtype_questionpy
* @author Maximilian Haye
* @copyright 2024 TU Berlin, innoCampus {@link https://www.questionpy.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class generated_id_element extends form_element {
/**
* Trivial constructor.
*
* @param string $name
*/
public function __construct(
/** @var string $name */
public string $name
) {
}

/**
* Render this item to the given context.
*
* @param render_context $context target context
* @package qtype_questionpy
*/
public function render_to(render_context $context): void {
$mangledname = $context->mangle_name($this->name);
$value = $context->moodleform->optional_param(
$mangledname,
null,
PARAM_ALPHANUMEXT
) ?? $context->data[$this->name] ?? $context->generate_uuid();

$context->add_element('hidden', $mangledname, $value);
$context->set_type($this->name, PARAM_ALPHANUMEXT);
}
}
2 changes: 2 additions & 0 deletions tests/data_provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use qtype_questionpy\form\conditions\is_not_checked;
use qtype_questionpy\form\elements\checkbox_element;
use qtype_questionpy\form\elements\checkbox_group_element;
use qtype_questionpy\form\elements\generated_id_element;
use qtype_questionpy\form\elements\group_element;
use qtype_questionpy\form\elements\hidden_element;
use qtype_questionpy\form\elements\option;
Expand Down Expand Up @@ -143,5 +144,6 @@ function element_provider(): array {
['static_text', new static_text_element('my_text', 'Label', 'Lorem ipsum dolor sit amet.')],
['input', new text_input_element('my_field', 'Label', true, 'default', 'placeholder')],
['textarea', new text_area_element('my_field', 'Label', true, 'default', 'placeholder')],
['id', new generated_id_element('my_id')],
];
}
9 changes: 9 additions & 0 deletions tests/form/elements/html/id.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><form id="my_form" autocomplete="off" action="" method="post" accept-charset="utf-8" class="mform">
<div style="display: none;"><input name="qpy_form[my_id]" type="hidden" value="24daab97-7eeb-422d-a2f3-f4e770fb11f6">
<input name="sesskey" type="hidden" value="sesskey">
<input name="_qf__qtype_questionpy_form_elements_test_moodleform" type="hidden" value="1">
</div>


</form></body></html>
4 changes: 4 additions & 0 deletions tests/form/elements/json/id.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"kind": "id",
"name": "my_id"
}
1 change: 1 addition & 0 deletions tests/form/elements/test_moodleform.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public function __construct(qpy_renderable $element) {
*/
protected function definition() {
$context = new root_render_context($this, $this->_form, 'qpy_form', []);
$context->uuidgen = fn() => '24daab97-7eeb-422d-a2f3-f4e770fb11f6';
$this->element->render_to($context);
}
}

0 comments on commit 18bb7da

Please sign in to comment.