Extending Moodle forms
Tags: moodle, php, webdev, tutorial
Published: 2023-04-27 10:51:43

While not possible on every forms, most of the existing moodle forms are extendable whether by extending classes or by using callbacks - which in Wordpress are known by _hooks._ Also, Moodle forms are based on [PEAR's QuickForm API](https://pear.php.net/manual/en/package.html.html-quickform.introduction.php) so that can be useful information. I learned that recently (well all my dev work on Moodle _is_ recent) when needed to add 2 new form fields on different contexts: in the new user auto-registration form and also when editing or adding an assignment activity.

I started by checking if any of the [existing callbacks](https://docs.moodle.org/dev/Callbacks) could help me with this and I was able to find `_type_myplugin_extend_signup_form()` and `type_myplugin_coursemodule_standard_elements()`.

## Extending Sign-up form

The first one would allow me to add an extra field in the registration form. Simply:
```php
function theme_stream_extend_signup_form($mform) {
    // Inject a static element of the word 'injected'
    $html = '<div class="alert alert-primary" role="alert">
        This is a primary alert—check it out!
         </div>';
    $mform->addElement('static', 'injectedstatic', $html);
 }
```
This is great! It works and adds a new static element identified by an id generated based on 'injectedstatic' as a last item on my sign up form. But it makes sense to have the warning before the relevant field, so how can I move my static element before, let's say the email field? JS could be used to move that item, but what horrendous hack would that be... Moodle should have a way right?

I'll give you `insertElementBefore()` Like you can imagine, this adds a form element before an existing one (if you want to move it around you would need to remove it before).

However, you also need to create it before inserting it so your code should be:
```php
function theme_stream_extend_signup_form($mform) {
    // Inject a static element of the word 'injected'
    $html = '<div class="alert alert-primary" role="alert">
        This is a primary alert—check it out!
         </div>';
    $warning = $mform->createElement('static', 'warning', $html);
    $mform->insertElementBefore($warning, 'email', $html);
 }
``` 
At this point, it is important to note that if you want to add a new field to your sign-up form, the best approach would be to create a custom profile field and make it available in the sign-up process/page. No code and 100% functional - custom profiles will be placed last on the form, but using the callback you can `removeElement()` and `insertElementBefore()`. 
```php
$mform->insertElementBefore($mform->removeElement('country'), 'email');
```
##Extending Mod Assign form
Imagine that I want to add a warning when people add or edit an assignment. The form fiddling is pretty much done the same way, so the only difference is that we need to check if this form belongs to mod_assign or not because `type_myplugin_coursemodule_standard_elements()` is called on every mod.

For that, we do `$cm = $formwrapper->get_current();` to get current form information and with a simple if we check `if ($cm->modulename == 'assign') {}` 

The final code could be something like this:
```php
function type_myplugin_coursemodule_standard_elements(moodleform_mod $formwrapper, MoodleQuickForm $mform) {
    $cm = $formwrapper->get_current();
    // Skip right away.
    if (!isset($cm) || $cm->modulename != 'assign') {
        return;
    }

    if ($cm->modulename == 'assign') {

        $html = '<p>my extra info</p>';
        $element= $mform->createElement('static', 'extraelement', '', $html);
        $mform->insertElementBefore($element, 'teamsubmission');

    }
}
```