Custom Plugin Creation

Individual functionality through individual Visforms custom plugins

Visforms has an extensive own event system. It enables visforms to be specifically extended with individual code.

More on this in: Event system for the Workflow.

Custom Submit Handler - Example of developing a Visforms custom plugin

For example, simply by using a Visforms custom plugin, you can transfer your own update-safe submit handler to the form. The requirements of what a separate submit handler should do are very different between two Visforms users. Therefore, you have to code what you want your submit handler to do yourself: you have to write an individual Visforms custom plugin. This requires some experience in programming with PHP and JavaScript or at least the willingness to familiarize yourself with both.

To help you get started, we’ve created a plugin template. In the following article we try to explain step by step how to customize our plugin template for your needs. We also provide concrete code examples for implementing some commonly requested submit handler actions.

Instructions for creating a plugin

Download the Plugin template for Joomla 4 and unzip the ZIP file: Download Plugin Master. This ZIP archive contains three files:

  • a index.html,
  • a plgmaster.xml and
  • a plgmaster.php.

The index.html has the sole function of protecting the directory in which the plugin will later be installed from unauthorized access. With the existence of this file, one can no longer see from the outside what files the directory contains.
You do not have to change this file.

The plgmaster.xml is the template for the so-called manifest file with the so-called manifest data. It is absolutely necessary when installing the plugin.

It contains important information for installing the plugin such as

  • the name,
  • the plugin type,
  • the files to install,
  • the plugin version.

It also contains so-called meta information about the plugin such as

  • the date when it was developed and
  • by whom it was developed.

The plgmaster.php contains the actual code that is executed by the plugin.

Give the plugin a name

The first thing you should do is give your plugin its own name. This should be one word in lowercase, such as “myvisformssubmithandler”. In the template, the name is “plgmaster”. In order to give the plugin its own name so that Joomla can install and use it under that name, you need to replace the string “plgmaster” with your own plugin name in many places.

First, rename the plgmaster.xml und plgmaster.php files by replacing the string “plgmaster” with your own plugin name. Then replace the string “plgmaster” in the following places within these two files. To do this, open the renamed file with an editor program of your choice.

In the renamed plgmaster.xml file you have to replace the string “plgmaster” in 3 places:

  • in <name>plgmaster</name> and
  • in <filename plugin="plgmaster">plgmaster.php</filename>.

In the renamed plgmaster.php file you need to replace the string “Plgmaster” in 1 place:

  • in the name of the PHP class class plgVisformsPlgmaster extends JPlugin.

For better readability you should use your plugin name with a capital letter at the beginning. However, the code also works if the class name contains only lowercase letters.

Adjust meta information

You should then adjust the following meta information in the renamed plgmaster.xml file. You do this by changing the value in the respective XML node according to your requirements.

A so-called XML node always consists of

  • an opening element (e.g. <author>),
  • a closing element (e.g. </author>) and
  • a text in between.

Adjust the texts in the following XML nodes. Some information is not absolutely necessary and can also be omitted entirely by simply deleting the entire XML node.

  • <author>: Author
  • <creationDate>: Creation date
  • <copyright>: Copyright information (can also be omitted)
  • <license>: license type (can also be omitted)
  • <authorEmail>: Email of the author (can also be omitted)
  • <authorUrl>: URL of the author (can also be omitted)
  • <description>: Text that is displayed in the administration as a description of the plugin

Leave all other XML nodes as they are.

Write the plugin code

Now that all the basic conditions for the correct installation of the plugin have been set, you can write the actual code for your submit handler. Open the renamed plgmaster.php file and take a quick look at the demo code.

Function onVisformsFormPrepare()

Code in the public function onVisformsFormPrepare($context, $form, $menuparams) function is executed before the form is rendered. In this function you can do all sorts of manipulations on the form and fields. You can also add custom JavaScript to the page here.

For example, you can use custom JavaScript to add additional hidden controls to the form. The values of a hidden control field can be dynamically set by your JavaScript code and submitted in the POST. Or you can block the sending process.

Function onVisformsBeforeFormSave()

Code in the public function onVisformsBeforeFormSave($context, $form, $fields) function is executed before the actual processing of the data submitted with the form begins on the server.

The processing that begins later includes about

  • Save data,
  • send emails,
  • Generate PDFs and
  • Show result text.

Here you can manipulate this process. For example, by changing the form parameters dynamically or by carrying out your own server-side security checks.

The template code of the onVisformsFormPrepare() function

public function onVisformsFormPrepare($context, $form, $menuparams)	{
    // Skip plugin if context is wrong
    $allowedContexts = array('com_visforms.form', 'mod_visforms.form', 'plg_vfformview.form');
    if (!in_array($context, $allowedContexts)) {
        return true;
    }
    $app = JFactory::getApplication();
    // only perform action, if we are in front end
    if ($app->isClient('administrator')) {
        return true;
    }
    // if $form->parentFormId is not set, Visforms or Content Plugin Form View version is to old
    if (!isset($form->parentFormId)) {
        return true;
    }
    // get value of id attribute of the form which is going to be displayed for further use
    $parentFormId = $form->parentFormId;
    // START: add custom submit handler function to the form	
    $script = 'jQuery(document).ready(function () {
        // add custom submit action function to form
        window["' . $parentFormId . 'SubmitAction"] = function (form) {
        
          // PLACE YOUR CODE IN HERE
          
          // return false: to prevent form from being submitted
          // return true: if submithandler performs another action but form should be send
          return true;
        };
    });';
    $wa = Factory::getApplication()->getDocument()->getWebAssetManager();
    $wa->addInlineScript($script);
    // END: add custom submit handler function to the form	
}

The $context parameter

The $context parameter contains information about where the onVisformsFormPrepare function was called from.
The different call points to display a Visforms form can be:

Among other things, this parameter can be used to control that an individual submit handler is only added to the forms that are displayed via a module.

  • $context === ‘com_visforms.form'
    Form is displayed via menu item of type Visforms » Form.
  • $context === ‘mod_visforms.form'
    Form is displayed via a Visforms Module.
  • $context === ‘plg_vfformview.form'
    Form is displayed in an article via the Content Plugin - Visforms Form View.

In principle, you should always make sure that your plugin code only runs when it is actually needed.
You can use the variable $allowedContexts to control the cases in which the plugin code should run.

The $form parameter

The parameter $form contains the complete form.
With $form->id you can access the ID of the currently displayed form.
With the value from $form->parentFormId you know the HTML id attribute of the <form> element.
The <form> element is the outermost HTML wrapper of the actual form.

For example, you can use the if ($form->id !== 1) {return true;} condition.
This ensures that the plugin code is only executed for the form with ID 1.
Or use the form ID to run different code for different forms.

if ($form->id === 1) {
    // your actions for form 1 ...
}
if ($form->id === 2) {
    // your actions for form 2 ...
}

The $menu_params parameter

The parameter contains all administrative settings of the menu with which the form was called. It is not used for a plugin that implements a custom submit handler. You can use this parameter to influence, among other things, whether the form title is displayed or not.

The JavaScript submit handler function

When a user tries to submit the form, the user input is first validated on the browser side with JavaScript. Then the Visforms code checks if there is an additional submit handler function. The check looks for a form-specific JavaScript function with a special name.

The searched function name consists of the ID of the HTML form element $parentFormId and the word SubmitAction. If such a JavaScript function is available, it will now be executed. It can be used to perform special actions.

Depending on the return value of the function, the form will be submitted or not.

  • If the return value is true, the form is submitted normally.
  • If the return value is false, sending is prevented.

So please always make sure that your function has the correct return value.

The template code of the onVisformsBeforeFormSave() function

public function onVisformsBeforeFormSave($context, $form, $fields) {
    // Skip plugin if context is wrong
    $allowedContexts = array('com_visforms.form', 'mod_visforms.form', 'plg_vfformview.form');
    if (!in_array($context, $allowedContexts)) {
        return true;
    }
    $app = JFactory::getApplication();
    // only perform action, if we are in front end
    if ($app->isClient('administrator')) {
        return true;
    }
    // PLACE YOUR CODE IN HERE
    return true;
}

For the parameters see above in the description of the onVisformsFormPrepare function.
The $form->parentFormId parameter is only required in the frontend. It doesn’t exist here.

Sample Code 1: Prevent Submit by non-logged-in users

Two alternative possibilities are shown.

You can achieve particular security if you use both options at the same time:

  • Possibility Number 1: The message appears in a modal JavaScript window immediately after clicking the ‘Submit’ button.
    Here, before the form is sent, the form is prevented from being sent directly in the browser.
    Basically, there is a risk here that the page will be manipulated and a sending can be forced as a result.
  • Possibility 2: The message appears as part of the page after the form has been submitted and rejected on the server.
    This is checked on the server only after the form has been sent.
    In principle, there is no risk here that the page will be manipulated and a sending can be forced as a result.

Option 1: Customize the onVisformsFormPrepare() function

Replace the template code in the renamed plgmaster.php
in between
// START: add custom submit handler function to the form
and
// END: add custom submit handler function to the form
with the code below.

This code checks if the user filling out the form is logged-in. If the user is not logged in, a JavaScript function is added to the form. The function from our example outputs a message and prevents the form from being sent.

// START: add custom submit handler function to the form	
$user = JFactory::getUser();
// user is not logged on. Add JavaScript that prevent form from being send
if (!$user->id) {
    $script = 'jQuery(document).ready(function () {
        // add custom submit action function to form
        window["' . $parentFormId . 'SubmitAction"] = function (form) {
            alert("Please log in first");
            return false;
        };
    });';
    $wa = Factory::getApplication()->getDocument()->getWebAssetManager();
    $wa->addInlineScript($script);
}
// END: add custom submit handler function to the form

Option 2: Customize onVisformsBeforeFormSave() function

Replace // PLACE YOUR CODE IN HERE with the following code:

$user = JFactory::getUser();
if (!$user->id) {
    $message = 'Please log in first';
    $app = JFactory::getApplication();
    $input = $app->input;
    $return = $input->post->get('return', null, 'cmd');
    $url = (!empty($return)) ? base64_decode(strtr($return, '-_,', '+/=')) :  'index.php';
    $app->enqueueMessage($message, 'warning');
    $app->redirect(JRoute::_($url, false));
    $app->close();
}

Sample Code 2: Add a hidden field and set its value dynamically

The example assumes that there are 2 different submit buttons under the form. Depending on which of the two buttons the user uses, a different value is set in the dynamically added hidden field. This value can then be evaluated later in the PHP code.

In our example, emails are sent when the user clicks the 2nd button.

To do this, replace the template code in the renamed plgmaster.php
in between
// START: add custom submit handler function to the form
and
// END: add custom submit handler function to the form
with the code below.

The advantage of using a dynamically generated hidden field is that it is not known to Visforms. Therefore, it is not stored in the database or transmitted with emails. The 2nd submit button receives the CSS class formready.

Customize onVisformsFormPrepare() function

$script = 'jQuery(document).ready(function () {
    let el = document.createElement("input");
    el.id = "'. $parentFormId .'myhiddenfield"
    el.type = "hidden"; 
    el.value = "0"; 
    el.name = "myhiddenfield";
    jQuery("#'. $parentFormId .'").append(el);
    window["'. $parentFormId .'SubmitAction"] = function (form) {
        if (jQuery(form.submitButton).hasClass("formready")) {
            jQuery("#'. $parentFormId .'myhiddenfield").val("1");
        };
        return true;
    };
});';
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->addInlineScript($script);

Customize onVisformsBeforeFormSave() function

Replace // PLACE YOUR CODE IN HERE with the following code:

$sendMail = $app->input->post->get('myhiddenfield', "0", 'STRING');
if (!empty($sendMail)) {
    // enable sending the result mail 
    $form->emailresult = 1;
    // enable sending the user mail 
    $form->emailreceipt = 1;
}

Completion and installation

Finally, you have to pack your new plugin, install it on the Joomla instance and test it.

  • Create a zip from the directory with the changed files.
  • Install the extension via Joomla Extensions Manager.
  • Publish the extensions in the Joomla Extensions Manager.
  • Test the plugin.