Working with modules

When extending Torro Forms, you’ll most definitely get in contact with modules. Modules are the foundation for any form integration or behavior that is not dependent on a content object like the form, its containers or elements (learn more about the plugin’s object types). Modules (some of which have submodules) usually provide several settings where the administrator (or whoever is permitted to edit forms in the backend) can control whether a given form should use that module/submodule and how it should behave.

Available Modules

Torro Forms includes five base modules:

  • Access Controls define who can enter a submission for a form and thus specify certain restrictions (for example restricting a form to logged-in users only or allowing only a limited maximum of submissions).
  • Actions are something that happens when a form submission is completed, usually involving the submission data being sent or forwarding the user to another location (for example sending to the admin via email or forwarding to a third-party REST API).
  • Protectors are security measures to prevent form submission spam (for example a honeypot field or an integration with the Google reCAPTCHA service).
  • Evaluators allow to create statistics and graphs for submissions of a form (for example the amount of responses in a given timeframe or how many people chose certain responses in a multiple choice question).
  • Form Settings are miscellaneous settings that specify more basic form behavior (for example the labels to use for navigation buttons or which feedback messages to display).

While you could implement additional modules, this is only recommended for extremely custom functionality that wouldn’t fit into any of the above. Most likely, your use-case would be covered to be implemented as a submodule to one of the modules.

Available Submodules

All of the above modules but the Form Settings module have submodules, and that’s where your main integration point is. To give an example, the Actions module has two submodules bundled into Torro Forms: The Email Notifications submodule sends an email with form submission data to one or more people, the Redirection submodule redirects the user to a certain page or URL after submitting the form. So imagine you want to implement an integration where form submission data is sent to a service API like MailChimp’s – this would be a perfect fit for a new submodule of the Actions module. Or, if you wanted to measure how quickly users hit the Submit button after initiating a form – a perfect fit for a submodule of the Evaluators module.

Before we dive into more technical details on how to implement those, let’s look at the available submodules Torro Forms provides:

Access Controls

  • The User Identification submodule prevents multiple submissions by a single user.
  • The Members submodule restricts the form to logged-in users only, possibly even to only selected users or users of certain roles.
  • The Timerange submodule restricts form submissions to only be available within a certain timeframe specified by a given start and/or end date.
  • The Submission Count submodule limits the maximum allowed number of submissions so that no more submissions can be created afterwards.

Actions

  • The Email Notifications submodule sends an email with form submission data to one or more people.
  • The Redirection submodule redirects the user to a certain URL.

Protectors

  • The reCAPTCHA submodule integrates with the Google reCAPTCHA service to identify bots and prevent their submissions.
  • The Honeypot submodule includes an additional hidden field in the form which implicitly invites bots to fill it, easily identifying those.
  • The Timetrap submodule sets a minimum amount of seconds users should spend on the form page before submitting in order to not be identified as spam.
  • The Linkcount submodule checks the form submission data for a threshold of included links and restricts it if there are too many.

Evaluators

  • The Participation submodule aggregates the number of submissions made for a form, providing a graph view split up into months, years and total.
  • The Element Responses submodule provides diagrams for every single-choice or multiple-choice question in a form, indicating which choices were picked by how many users.

As a reminder, the Form Settings module works differently and doesn’t include submodule functionality.

Accessing modules and submodules

To access the module and submodule class instances registered with the plugin, your entry point is the module manager, accessible via torro()->modules(). You can then access any registered module via a magic method on the module manager, using the slug under which the module is registered. For the existing modules, that would be either access_controls(), actions(), protectors(), evaluators() or form_settings(). As a full example, to get the Actions module instance, use torro()->modules()->actions(). All module classes are derived from a common module base class to have similar functionality and a structured foundation.

You can also register new modules via torro()->modules()->register( $slug, $module_class_name ), however be reminded that this is hardly ever necessary. Most likely a submodule for one of the existing modules is a better fit.

In order to retrieve an existing submodule instance, you need to call a get( $slug ) method on the module instance that the submodule is registered with. For example, to get the Email Notifications submodule instance, use torro()->modules()->actions()->get( 'email_notifications' ). Note that the methods to handle submodules only exist on modules that support submodules – so the Form Settings module doesn’t have them.

In most cases, there isn’t anything you’d wanna do with existing modules or submodules – you’d usually wanna implement your own submodules and register them.

Implementing your own submodule

To create a custom submodule, you need to implement it as a class and then register that class with the module that it should be part of. In order to do that, you need to use the register( $slug, $submodule_class_name ) method on the module. So if you had implemented a MailChimp integration sending form submission data to their API in a class called MyPlugin\Actions\MailChimp, you would wanna register it with torro()->modules()->actions()->register( 'mailchimp', 'MyPlugin\Actions\MailChimp' ).

The class for the submodule must extend the base class for that type of submodule. While there is a class awsmug\Torro_Forms\Modules\Submodule, this is only the very base class for any submodule, but each module also has its own submodule base class extending that class. So you only need to worry about the more specific classes.

Access Controls

If you’re implementing an access control, you need to extend the awsmug\Torro_Forms\Modules\Access_Controls\Access_Control base class.

Its core is a can_access( $form, $submission = null ) method which is passed the form object that is currently being tried to access, and optionally the submission object for that form (if an existing submission). The method should return a boolean or WP_Error object depending on whether your access control determined the user can or cannot access the form.

Actions

If you’re implementing an action, you need to extend the awsmug\Torro_Forms\Modules\Actions\Action base class.

Its core is a handle( $submission, $form ) method which is passed the submission object that has just been completed, and the form object that submission is for. The method should do what it needs to do (for example send the submission data to an external API) and then return a boolean or WP_Error object depending on whether the process finished successfully or not.

Protectors

If you’re implementing a protector, you need to extend the awsmug\Torro_Forms\Modules\Protectors\Protector base class.

Its core is a verify_request( $data, $form, $submission = null ) method which is passed the raw POST data submitted, the form object for that a submission is tried to be sent, an optionally the submission object for that form (if an existing submission). The method should return a boolean or WP_Error object depending on whether the request is valid or considered spammy.

There’s a second method render_output( $form ) which is passed the form object currently accessed. You must implement that method as well, and it can be used to print additional HTML output into the page, for example a hidden field with some data or the markup for another security integration that you can later verify via verify_request().

Evaluators

If you’re implementing an evaluator, you need to extend the awsmug\Torro_Forms\Modules\Evaluators\Evaluator base class.

Its core is an evaluate_single( $aggregate_results, $submission, $form ) method which is passed an array with results that have been aggregated so far by the evaluator, the completed submission object, and the form object that submission is for. The method needs to evaluate the submission and add the results to the passed results array, and at the end of the method return that array. When the method is called for the next submission, that same array will be passed – this way you aggregate results over time.

There’s a second method that needs to be implemented: With show_results( $results, $form, $args = array() ) you actually display the aggregated results in the way you prefer, for example as a table or a graph. The method receives a results array which is exactly the array that has been aggregated via evaluate_single() over time, it also receives the form object and an array of arbitrary display arguments that you can then handle. The Shortcode is missing a form ID! shortcode can then pass these arguments when display results of your evaluator.

Common methods for any submodule

In addition to the above methods which are specific to the type of submodule (i.e. which module it is part of), there are a number of methods that can be implemented and used on any submodule of the above modules.

Form options (i.e. form metadata)

You can implement a get_meta_fields() method, which should return an array of field definitions which will then be displayed as meta fields in the form editing screen, under a tab for the submodule. The field definitions should follow the regular pattern of the field library used in the plugin. With the meta fields you can give the administrator control about certain behavior of your submodule for the form. All submodule base classes of the above modules implement this method already, adding a checkbox field with the slug ‘enabled’, providing a simple toggle for the administrator to enable/disable the submodule for a form – so make sure to always call the parent method in the beginning of your implementation.

There’s a get_form_option( $form_id, $option, $default = false ) method implemented that you can use to get the current value of a form option that you defined a field for in get_meta_fields(). You can also use get_form_options( $form_id ) to get an array of current values for all form options defined by your submodule.

There’s also an enabled( $form ) method implemented in all four above submodule base classes, which is passed the form object. This method determines whether the submodule is enabled for the given form – if it isn’t, none of its functionality will be executed. The implementation present calls get_form_option( $form->id, 'enabled', false ) and simply returns that value. Remember, that’s controlled by the meta field that the base classes make available.

Sometimes you may have another meta field that is already sufficient to determine whether a submodule is active, making the default ‘enabled’ checkbox redundant. In that case, you can simply unset that field in your custom get_meta_fields() implementation, and then override the enabled() method to use whatever logic is necessary to determine whether the submodule is enabled or not.

Options

Sometimes your submodule may not only require form options, but also regular options that would appear in a tab in the dedicated Torro Forms settings screen. Regular options should only be used in a submodule if they are something that needs to be set globally, not per form. A good example would be API keys for a custom integration – you wouldn’t typically wanna set those again for every form.

If your submodule requires any options, you should implement get_settings_sections() and get_settings_fields() methods. The first method should return an array of section definitions (each section containing minimally a ‘title’ key), the latter method should return an array of field definitions (again following the regular pattern of the plugin’s field library). For each field you should also specify a ‘section’ key, referring to the identifier of one of the sections you defined in the other method. Those sections and fields will then be automatically rendered under a tab for the submodule in the plugin’s settings screen.

There’s a get_option( $option, $default = false ) method implemented that you can use to get the current value of an option that you defined a field for in get_settings_fields(). You can also use get_options() to get an array of current values for all options defined by your submodule.