Craft 3
Sprout Forms installation and update instructions for Craft 3.
# Requirements
- Craft CMS 3.3.0 or a more recent version of Craft
# Installation via Plugin Store
- Find the plugin in the Plugin Store in your Craft Control Panel
- Go to Settings → Plugins and select “Install”
# Installation via Composer
- Open your terminal and go to your Craft project:
cd /path/to/project
- Then tell Composer to load the plugin:
composer require barrelstrength/sprout-forms
- In the Control Panel, go to Settings → Plugins and select “Install”
# Upgrading to Craft 3
BEFORE YOU UPDATE
Make sure you have updated to the latest version of Sprout Forms 2.x before updating to Sprout Forms 3.x for Craft 3
# Form Field changes
In Craft 2, Sprout Forms extended the Craft Field Layout Editor and allowed you to add any field that Craft supported to a Form. While you could add any field on the back-end, Sprout only supported output the HTML for a subset of the fields on the front-end. In particular, more advanced fields like the Matrix had to supported manually in the templates.
In Craft 3, Sprout Forms added a new Form Field API that now requires Form Fields to have both front-end and back-end support. If you happen to be using a more advanced field type that was supported by Craft but not by Sprout Forms in Craft 2, you may need to develop a Custom Form Field to support that field in Craft 3.
# Template Changes
Read over the Changes in Craft 3 (opens new window) for a general overview of things that have changed. These changes have affected Sprout Forms in several ways and if you have built any Forms with custom HTML you will likely need to update your code.
The Action URL format and Redirect URL format
<form method="post" accept-charset="utf-8" enctype="multipart/form-data">
{{ csrfInput() }}
<input type="hidden" name="action" value="sprout-forms/entries/save-entry">
{{ redirectInput("/contact?message=thankyou") }}
<input type="hidden" name="handle" value="formHandle">
...
<input type="submit" value="Submit">
</form>
Retrieving a form
{% set form = craft.sproutForms.form('contact') %}
Identifying a field by type
Where field
is a Form Field model:
Displaying Tabs and Fields in Custom Templates
{# form.html #}
{%- for tab in form.getFieldLayout().getTabs() %}
{{ craft.sproutForms.displayTab(form, tab.id, renderingOptions) }}
{% endfor -%}
{# tab.html #}
{% for field in layoutFields -%}
{{- craft.sproutForms.displayField(form, field, renderingOptions) }}
{%- endfor %}
# Notification Email Changes
The default Notification Email will now exclude several fields that may include sensitive or irrelevant information for a Notification Email. These fields include:
- Custom HTML
- Hidden Field
- Invisible Field
- Private Notes Field
- Section Heading Field
If you require these fields to be displayed in a Notification Email or wish to update the logic around which of them that do, you will need to override the default template and customize it to your needs. See this response (opens new window) on Stack Exchange for more info.
# Upgrading to Forms 3.4.1
Sprout Forms v3.4.1 introduces a handful of changes to make the Integrations API more flexible for custom Integrations. The general Integration architecture has stayed the same but several methods have been renamed. Please be sure to test any Custom Integrations while upgrading to 3.4.1 and consider the following changes:
# Removed
- Removed base Integration
updateTargetFieldsAction
andupdateSourceFieldsAction
dependencies in favor ofupdateTargetFieldsOnChange
to allow Integrations to dynamically target fields to watch for changes - Removed
prepareFieldTypeSelection
andprepareIntegrationTypeSelection
variables and methods and simplified how Integration Types field is populated in modal templates
# Moved
- Moved
getFieldsAsOptionsByRow
,getCompatibleFields
, andgetTargetIntegrationFieldsAsMappingOptions
fromIntegrationsController
toEntryElementIntegration
class
# Renamed
- Renamed
getFormFieldsAsMappingOptions
=>getSourceFormFieldsAsMappingOptions
- Renamed
actionGetElementEntryFields
=>actionGetTargetIntegrationFields
and updated it to instantiate and populate an Integration dynamically and trigger thegetTargetIntegrationFieldsAsMappingOptions
on a given Integration - Renamed
prepareFieldMapping
=>refreshFieldMapping
and moved to base Integration class init method - Renamed
resolveFieldMapping
=>getTargetIntegrationFieldValues
and moved to base Base Integration class - Renamed IntegrationTrait property
entry
=>formEntry
# Upgrading to Forms v3.5.0
Sprout Forms v3.5.0 adds support for Field Rules and includes updates to the Form Templates and Email Templates to support the new conditional behavior. In addition, Form Templates have been optimized to simplify the amount of javascript required in the templates. Form Templates and all javascript can still be customized however you need.
If your Forms use the Form Templates and Email Templates provided by Sprout Forms, no updates are needed. For Forms using custom Form Templates:
# Form Templates
Several updates have been made to the Form Templates to optimize the javascript used in the templates and add support for Field Rules.
Added support for rules
ADDED RULES VARIABLE
{%- set rules = form.getRules() -%}
ADDED RULES DATA ATTRIBUTE
<form method="post" accept-charset="utf-8" enctype="multipart/form-data"
{%- if id %} id="{{ id }}"{% endif -%}
{%- if class %} class="{{ class -}}{%- if errors %} {{ errorClass }}{% endif -%}"{% endif -%}
data-rules="{{ rules|json_encode|raw|e('html') }}"
>
Updated javascript
The latest form templates remove most JS from the templates and now include it via files. JS classes that require initialization have been simplified to only require the ID. The JS has also been updated to set the CSRF token name. The CSRF token value will be retrieved dynamically as necessary.
OLD
<script>
// ... Lots of JS
</script>
NEW
{%- set accessibilityJsUrl = view.getAssetManager().getPublishedUrl(
'@sproutforms/web/assets/formtemplates/dist/js/accessibility.js',
true) -%}
{%- do view.registerJsFile(accessibilityJsUrl, {
position: 3
}) -%}
{%- set rulesJsUrl = view.getAssetManager().getPublishedUrl(
'@sproutforms/web/assets/formtemplates/dist/js/rules.js',
true) -%}
{%- do view.registerJsFile(rulesJsUrl) -%}
{%- set addressFieldJsUrl = view.getAssetManager().getPublishedUrl(
'@sproutforms/web/assets/formtemplates/dist/js/addressfield.js',
true) -%}
{%- do view.registerJsFile(addressFieldJsUrl) -%}
{% js at endBody %}
(function() {
window.csrfTokenName = "{{ craft.app.getConfig().getGeneral().csrfTokenName|e('js') }}";
new SproutFormsAddressField('{{ id }}');
new SproutFormsCheckableInputs('{{ id }}');
new SproutFormsRules('{{ id }}');
})();
{% endjs -%}
# Email Templates
Email Templates can now check if a field is hidden by a field rule:
OLD
{% for field in fields if not field.isPlainInput() %}
NEW
{% for field in fields if not field.isPlainInput() and not entry.getIsFieldHiddenByRule(field.handle) %}
# Other changes
- Removed
barrelstrength\sproutforms\elements\Form::deleteById()
. Use Elements ServicedeleteElementById
instead. - Removed
barrelstrength\sproutforms\web\twig\variables\getIntegrationById()
. Use Integrations service via Template API instead.
Custom integrations may also need to update how they initialize the target field column in the Field Mapping table:
// OLD
new Craft.SproutForms.Integration({
integrationType: '{{ className(integration)|e('js') }}'
});
// NEW
new SproutFormsIntegration({
integrationType: '{{ className(integration)|e('js') }}'
});
# Updating to Forms v3.6.0
This release adds several improvements to Spam workflows and has touched on several areas of the Entry Submission workflow. Some breaking changes have been introduced so users with more custom form implementations will want to review what has changed to get things updated properly.
# Form Templates
Custom Form Templates will need to update the class name used in the default conditional logic to correctly hide fields used in conditional logic rules.
OLD
{% css %}
.hidden {
display: none !important;
}
{% endcss %}
NEW
{% css %}
.sprout-hidden {
display: none !important;
}
{% endcss %}
# Entry Queries using Status Handles
The statusHandle
attribute has been removed as the same behavior is available via the Element status
attribute.
OLD
{% for formEntry in craft.sproutForms.entries
.formHandle('contact')
.statusHandle('pending')
.all() %}
{{ formEntry.title }}
{% endfor %}
NEW
{% for formEntry in craft.sproutForms.entries
.formHandle('contact')
.status('pending')
.all() %}
{{ formEntry.title }}
{% endfor %}
# Custom Captchas
The Captcha API has been updated.
OLD (OnBeforeSaveEntryEvent)
$event->isValid = true;
$event->fakeIt = true;
$event->entry->addError(Entry::CAPTCHA_ERRORS_KEY, $errorMessage);
NEW (OnBeforeValidateEntryEvent)
// isValid is now handled by checking for Errors on the model
// fakeIt is now handled via the 'Spam Redirect Behavior' setting in the CP
$this->addError(self::CAPTCHA_ERRORS_KEY, $errorMessage);
Previously, captcha validation ran during the OnBeforeSaveEntryEvent
Event and used the isValid
and fakeIt
attributes on the event to control the flow of how the rest of the request got processed. Now, the active Captchas get processed on the OnBeforeValidateEntryEvent
Event, errors are added to the Captcha models, and the Captcha models are added to the Entry::captchas
attribute. This gives us a lot more flexibility on how to give the user control over this workflow in the CP and how we track errors when entries are flagged as Spam.
# Events
OnBeforePopulateEntryEvent
The isValid
and fakeIt
attributes have been removed as they were never used in the validation workflow.
REMOVED
$event->isValid
$event->fakeIt
OnBeforeValidateEntryEvent
The isValid
and fakeIt
attributes have been removed as they were never used in the validation workflow.
REMOVED
$event->isValid
$event->fakeIt
OnBeforeSaveEntryEvent
The fakeIt
attribute has been removed as it is not longer used in the validation workflow. Use the 'Spam Redirect Behavior' setting instead. The isValid
and errors
attributes remain on this event and can be used by developers as a final opportunity to add custom validation behavior. However, it is recommended to create a Custom Captcha and add errors to the Captcha model when verifySubmission
is validated.
REMOVED
$event->fakeIt
# Upgrading to Forms v3.9.0
Sprout Forms 3.9.0 is a recommended upgrade and fixes a vulnerability that could occur in some scenarios with custom Email Templates.
This release adds native support for AJAX submissions, global Success and Error Messages, and an improved Javascript submission event API. These features are provided out of the box if you are using our default Accessible Form Templates but will require manual updates to any custom template used in your project.
An updated Email Template API improves support for extending layouts from within your templates folder. If you have created any custom Email Templates Integrations, you will need to make a small change to the paths in your custom Email Templates Integration to support this behavior.
# AJAX Submissions
If you currently have a custom AJAX implementation to submit your forms, consider checking out our new AJAX submission feature and new Javascript Events. Let us know if it doesn't do something you need it to do. We'd love to help you have less code to manage!
Custom Form Templates will need to add support for AJAX submissions. Several individual Form settings (Submission Method, Success Message, Error Message, Error Display Method) may not work as expected until these updates are made to the templates.
Updates to form.twig
<!-- Add `data-submission-method` attribute to form tag -->
<form method="post" accept-charset="utf-8" enctype="multipart/form-data" data-submission-method="{{ form.submissionMethod }}">
...
</form>
Updates to form.twig
{# Register Submit Handler JS file #}
{%- set submitHandlerJsUrl = view.getAssetManager().getPublishedUrl(
'@sproutforms/web/assets/formtemplates/dist/js/submit-handler.js',
true) -%}
{%- do view.registerJsFile(submitHandlerJsUrl) -%}
...
{# Initialize SproutFormsSubmitHandler #}
{% js at endBody %}
(function() {
window.csrfTokenName = "{{ craft.app.getConfig().getGeneral().csrfTokenName|e('js') }}";
new SproutFormsAddressField('{{ id }}');
new SproutFormsCheckableInputs('{{ id }}');
new SproutFormsDisableSubmitButton('{{ id }}');
new SproutFormsRules('{{ id }}');
new SproutFormsSubmitHandler('{{ id }}');
})();
{% endjs -%}
# Global Success and Error Messages
The default Accessible Templates have been updated to support global Success and Error messages when using AJAX submission or when a form is redirected back to the same page after submission. This code could also be used as a starting point for similar behavior if redirecting to a different page after the form is submitted.
Updates to form.twig
{# Set these new variables at the top of the form.twig template #}
{% set globalErrorsEnabled = form.errorDisplayMethod in ['global', 'both'] ? true : false %}
{% set lastEntry = craft.sproutForms.lastEntry(form.id) %}
{# Define globalErrorListHtml if it exists #}
{%- set globalErrorListHtml -%}
{% set globalErrors = [] %}
{% for errors in entry.getErrors() %}
{% for error in errors %}
{% set globalErrors = globalErrors|merge([error]) %}
{% endfor %}
{% endfor %}
{% include "errors" with {
errors: globalErrors
} %}
{%- endset -%}
{# Conditionally output the global Success and Error messages within the form.twig template #}
{% if globalErrorsEnabled and errors|length and (form.errorMessage or globalErrorListHtml) %}
{% set errorMessage = view.renderObjectTemplate(form.errorMessage, entry) %}
<div id="{{ id }}-message-box" class="sproutforms-message-errors">
{{ errorMessage|markdown }}
{{ globalErrorListHtml }}
</div>
{% endif %}
{% if lastEntry and form.id == lastEntry.getForm().id and form.successMessage %}
{% set successMessageTemplate = form.successMessage ?? '' %}
{% set successMessage = view.renderObjectTemplate(successMessageTemplate, lastEntry) %}
<div id="{{ id }}-message-box" class="sproutforms-message-success">{{ successMessage|markdown }}</div>
{% endif %}
Updates to field.twig
{# Set this new variables at the top of the field.twig template #}
{% set inlineErrorsEnabled = form.errorDisplayMethod in ['inline', 'both'] ? true : false %}
{# Update the field.twig template to only output the errors if `inlineErrorsEnabled` is true #}
{% if inlineErrorsEnabled %}
{% include "errors" with {
errors: errors,
name: name
} %}
{% endif %}
# Javascript Events
Anything previously watching the submit
event should probably be update to use the new sproutFormsSubmit
event. As Sprout Forms watches the submit
event issues can arise if third-party javascript also attempts to watch the same event. See the new
OLD
form.addEventListener('submit', function(event) {
// ...
}, false);
NEW
form.addEventListener('onSproutFormsSubmit', function(event) {
// ...
}, false);
# Email Templates API
Sprout Forms v3.9.0 uses the updated Email Templates Integrations API. See the Sprout Email upgrade notes for details: Upgrading to Email v4.2.0.
# Upgrading to Forms v3.10.0
Captcha Config Overrides
While the file-based config config/sprout-forms.php
had not yet been documented, if you were using it to define Captcha settings these settings will need to be updated.
// OLD
'captchaSettings' => [
'sproutforms-duplicatecaptcha' => [
'enabled' => true,
],
'sproutforms-javascriptcaptcha' => [
'enabled' => true,
],
'sproutforms-honeypotcaptcha' => [
'enabled' => true,
'honeypotFieldName' => 'beesknees',
'honeypotScreenReaderMessage' => 'Leave this field blank'
],
],
// NEW
'captchaSettings' => [
'barrelstrength\sproutforms\captchas\DuplicateCaptcha' => [
'enabled' => true,
],
'barrelstrength\sproutforms\captchas\JavascriptCaptcha' => [
'enabled' => true,
],
'barrelstrength\sproutforms\captchas\HoneypotCaptcha' => [
'enabled' => true,
'honeypotFieldName' => 'beesknees',
'honeypotScreenReaderMessage' => 'Leave this field blank'
],
],
# Upgrading to Forms v3.12.0
Sprout Forms v3.12.0 includes a small breaking change to the getFrontEndInputHtml
method in your Form Field classes. This was necessary to support more refined targeting for Rendering Options including the targeting of error classes on input
fields to better support using Rendering Options for front-end frameworks such as Bootstrap.
# Custom Form Fields
Custom Form fields will need to update the getFrontEndInputHtml
method signature to include a new second argument for the Form Entry Element and pass that entry
variable to your input template:
// OLD
public function getFrontEndInputHtml($value, array $renderingOptions = null): Markup
{
$rendered = Craft::$app->getView()->renderTemplate('dropdown/input', [
'name' => $this->handle,
'value' => $value,
'field' => $this,
'renderingOptions' => $renderingOptions
]
);
return TemplateHelper::raw($rendered);
}
// NEW
use barrelstrength\sproutforms\elements\Entry;
public function getFrontEndInputHtml($value, Entry $entry, array $renderingOptions = null): Markup
{
$rendered = Craft::$app->getView()->renderTemplate('dropdown/input', [
'name' => $this->handle,
'value' => $value,
'field' => $this,
'entry' => $entry,
'renderingOptions' => $renderingOptions
]
);
return TemplateHelper::raw($rendered);
}
# Rendering Options Error Classes
Additionally, to support Rendering Options error classes in your custom Form Field input template, you will want to update the Form Field input template to check for errors and update the class
to include any error classes if they exist:
// OLD
{%- set class = renderingOptions.class ?? name %}
// NEW
{%- set class = renderingOptions.class ?? name %}
{%- set errors = entry is not empty ? entry.getErrors(name) : null %}
{%- set errorClass = renderingOptions.errorClass ?? null %}
{%- set class = errors and errorClass ? class~' '~errorClass : class %}
← Javascript Events FAQ →