diff --git a/Add-ons/UmbracoForms/Developer/Configuration/index-v7.md b/Add-ons/UmbracoForms/Developer/Configuration/index-v7.md
new file mode 100644
index 00000000000..1ea8c7ff5ce
--- /dev/null
+++ b/Add-ons/UmbracoForms/Developer/Configuration/index-v7.md
@@ -0,0 +1,118 @@
+---
+versionFrom: 7.0.0
+meta.Title: "Umbraco Forms configuration"
+meta.Description: "In Umbraco Forms it's possible to customize the functionality with various configuration values."
+---
+
+# Configuration
+With Umbraco Forms it's possible to customize the functionality with various configuration values.
+
+## Editing configuration values
+The configuration for Umbraco Forms can be changed by modifying the XML based config file found at `/App_Plugins/UmbracoForms/UmbracoForms.config`
+
+### UploadStorageDirectory
+This is *legacy and is no longer in use* - Forms that use an upload field will use the same IFileSystem as the [media section](../IFileSystem/#forms-containing-upload-fields)
+
+### IgnoreWorkFlowsOnEdit
+This configuration expects `True` or `False` and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the `AllowEditableFormSubmissions` configuration value.
+
+### ExecuteWorkflowAsync
+This configuration key is *experimental* and will allow Workflows to be executed in an async manner
+The value can be `True/False` or a list of form names that ignore workflows that are comma separated `form name,contact form`
+
+### DisableFormCaching
+This configuration value expects a `True/False` value and can be used to toggle if Forms should be read from the JSON representation on disk or from the relevant Forms IFileSystem. Forms are cached for 10 minutes.
+
+### DisableDefaultWorkflow
+This configuration value expects a `True/False` value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form.
+
+### DisableAutomaticAdditionOfDataConsentField
+This configuration value expects a `True/False` value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation.
+
+### AllowEditableFormSubmissions
+This configuration value expects a `True/False` value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to `True` it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. `?recordId=GUID` Replace `GUID` with the GUID of the form submission.
+
+:::warning
+Enable this feature ONLY if you do understand the security implications.
+:::
+
+### RecaptchaPublicKey & RecaptchaPrivateKey
+Both of these configuration values are needed in order to use the "*Recaptcha2*" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here - https://www.google.com/recaptcha/admin
+
+Google has renamed these recently and the `Site Key` refers to `RecaptchaPublicKey` and `Secret Key` is to be used for `RecaptchaPrivateKey`
+
+### RecaptchaV3SiteKey & RecaptchaV3PrivateKey
+Both of these configuration values are needed in order to use the "*reCAPTCHA V3 with Score*" field type implementing ReCaptcha V3 from Google. This field type is available in Umbraco Forms from v8.7+.
+
+You can obtain both of these values after signing up to create a ReCaptcha key here: https://www.google.com/recaptcha/admin.
+
+### DatePickerYearRange
+This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years.
+
+### EnableAntiForgeryToken
+This setting needs to be a `True` or `False` value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this and set this to `True`. Due to older versions of Umbraco Forms not containing this, it has become an optional config setting and due to upgrade reasons we do not automatically set this to `True` for you.
+
+If you do set this to `True` then you need to add `@Html.AntiForgeryToken()` to your forms. The default template for Forms can be found in `~/Views/Partials/Forms/Form.cshtml` and should have `@Html.AntiForgeryToken()` in the `@using (Html.BeginUmbracoForm [...]` block.
+
+### StoreUmbracoFormsInDb
+This setting needs to be set to `True` if you want your Forms data to be stored in the database instead of the .json files in the `App_Data/UmbracoForms` directory in the file system.
+
+For more information on this, read the [Forms in the Database](../Forms-in-the-Database) article.
+
+### UseLegacyPageService
+In Umbraco Forms 8.7 an update was made to improve the performance of the service responsible for retrieving the content of the Umbraco page where a form is hosted. This service is used to populate the string placeholders - or "magic strings" - with the values of properties from the page.
+
+By setting the value of the `UseLegacyPageService` to `True` the old service can be reinstated.
+
+### DisallowedFileUploadExtensions
+When using the File Upload field in a form, editors can choose which file extensions they want to accept. When an image is expected, they can for example specify that only `.jpg` or `.png` files are uploaded.
+
+There are certain file extensions that in almost all cases should never be allowed, which are held in this configuration value. This means that even if an editor has selected to allow all files, any files that match the extensions listed here will be blocked.
+
+By default, .NET related code files like `.config` and `.aspx` are included in this deny list. You can add or - if you are sure - remove values from this list to meet your needs.
+
+### MaxNumberOfColumnsInFormGroup
+Added in 8.7.0, this setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12.
+
+### DefaultTheme
+Added in 8.8.0, this setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme.
+
+## Default Settings
+There are several configuration keys that start with `Default`. This allows you to configure the values for when a new form is created.
+
+### DefaultManualApproval
+This setting needs to be a `True` or `False` value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign.
+
+### DefaultDisableStylesheet
+This setting needs to be a `True` or `False` value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet.
+
+### DefaultMarkFieldsIndicator
+This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields
+* `NoIndicator`
+* `MarkMandatoryFields`
+* `MarkOptionalFields`
+
+### DefaultIndicator
+This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk `*`
+
+### DefaultRequiredErrorMessage
+This allows you to configure the required error validation message. By default this is `Please provide a value for {0}` where the `{0}` is used to replace the name of the field that is required.
+
+### DefaultInvalidErrorMessage
+This allows you to configure the invalid error validation message. By default this is `Please provide a valid value for {0}` where the `{0}` is used to replace the name of the field that is invalid.
+
+### DefaultShowValidationSummary
+This setting needs to be a `True` or `False` value and will allow you to toggle if the form will display all form validation error messages in a validation summary together.
+
+### DefaultHideFieldValidationLabels
+This setting needs to be a `True` or `False` value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid.
+
+### DefaultMessageOnSubmit
+This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node.
+
+### DefaultStoreRecordsLocally
+This setting needs to be a `True` or `False` value and will allow you to toggle if form submission data will be stored in the Umbraco Forms database tables. By default this is set to `True`.
+
+### DefaultAutocompleteAttribute
+
+Added in 8.8.0, this setting provides a value to be used for the `autocomplete` attribute for newly created forms. By default the value is empty, but can be set to `on` or `off` to have that value applied as the attribute value used when rendering the form.
diff --git a/Add-ons/UmbracoForms/Developer/Configuration/index-v9.md b/Add-ons/UmbracoForms/Developer/Configuration/index-v9.md
deleted file mode 100644
index 543d363573f..00000000000
--- a/Add-ons/UmbracoForms/Developer/Configuration/index-v9.md
+++ /dev/null
@@ -1,215 +0,0 @@
----
-versionFrom: 9.0.0
-meta.Title: "Umbraco Forms configuration"
-meta.Description: "In Umbraco Forms it's possible to customize the functionality with various configuration values."
-state: complete
-verified-against: beta-1
----
-
-# Configuration
-
-With Umbraco Forms it's possible to customize the functionality with various configuration values.
-
-## Editing configuration values
-
-All configuration for Umbraco Forms is held in the `appSettings.json` file found at the root of your Umbraco website. If the configuration has been customized to use another source, then the same keys and values discussed in this article can be applied there.
-
-The convention for Umbraco configuration is to have package based options stored as a child structure below the `Umbraco` element, and as a sibling of `CMS`. Forms configuration follows this pattern, i.e.:
-
-```json
-{
- ...
- "Umbraco": {
- "CMS": {
- ...
- },
- "Forms": {
- ...
- }
- }
-}
-```
-
-All configuration for Forms is optional. In other words, all values have defaults that will be applied if no configuration is available for a particular key.
-
-For illustration purposes, the following structure represents the full set of options for configuration of Forms, along with the default values. This will help when you need to provide a different setting to understand where it should be applied.
-
-```json
- "Forms": {
- "FormDesign": {
- "DisableAutomaticAdditionOfDataConsentField": false,
- "DisableDefaultWorkflow": false,
- "MaxNumberOfColumnsInFormGroup": 12,
- "DefaultTheme": "default",
- "Defaults": {
- "ManualApproval": false,
- "DisableStylesheet": false,
- "MarkFieldsIndicator": "NoIndicator",
- "Indicator": "*",
- "RequiredErrorMessage": "Please provide a value for {0}",
- "InvalidErrorMessage": "Please provide a valid value for {0}",
- "ShowValidationSummary": false,
- "HideFieldValidationLabels": false,
- "MessageOnSubmit": "Thank you",
- "StoreRecordsLocally": true,
- "AutocompleteAttribute": ""
- }
- },
- "PackageOptions": {
- "IgnoreWorkFlowsOnEdit": "True",
- "ExecuteWorkflowAsync": "False",
- "AllowEditableFormSubmisisons": false
- },
- "Security": {
- "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx",
- "EnableAntiForgeryToken": true,
- "SavePlainTextPasswords": false
- },
- "FieldTypes": {
- "DatePicker": {
- "DatePickerYearRange": 10
- },
- "Recaptcha2": {
- "PublicKey": "",
- "PrivateKey": ""
- },
- "Recaptcha3": {
- "SiteKey": "",
- "PrivateKey": ""
- }
- }
- }
-```
-
-## Form design configuration
-
-### DisableAutomaticAdditionOfDataConsentField
-
-This configuration value expects a `true` or `false` value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation. Defaults to `false`.
-
-### DisableDefaultWorkflow
-
-This configuration value expects a `true` or `false` value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form. Defaults to `false`.
-
-### MaxNumberOfColumnsInFormGroup
-
-This setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12.
-
-### DefaultTheme
-This setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme.
-
-### Form default settings configuration
-
-The following configured values are applied to all forms as they are created. They can then be amended on a per-form basis via the Umbraco backoffice.
-
-#### ManualApproval
-
-This setting needs to be a `true` or `false` value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign. Defaults to `false`.
-
-#### DisableStylesheet
-
-This setting needs to be a `true` or `false` value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet. Defaults to `false`.
-
-#### MarkFieldsIndicator
-
-This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields:
-
-* `NoIndicator` (default)
-* `MarkMandatoryFields`
-* `MarkOptionalFields`
-
-#### Indicator
-
-This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk `*`.
-
-#### RequiredErrorMessage
-
-This allows you to configure the required error validation message. By default this is `Please provide a value for {0}` where the `{0}` is used to replace the name of the field that is required.
-
-#### InvalidErrorMessage
-
-This allows you to configure the invalid error validation message. By default this is `Please provide a valid value for {0}` where the `{0}` is used to replace the name of the field that is invalid.
-
-#### ShowValidationSummary
-
-This setting needs to be a `true` or `false` value and will allow you to toggle if the form will display all form validation error messages in a validation summary together. Defaults to `false`.
-
-#### HideFieldValidationLabels
-
-This setting needs to be a `true` or `false` value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid. Defaults to `false`.
-
-#### MessageOnSubmit
-
-This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node. Defaults to `Thank you`.
-
-#### StoreRecordsLocally
-
-This setting needs to be a `True` or `False` value and will allow you to toggle if form submission data should be stored in the Umbraco Forms database tables. By default this is set to `True`.
-
-#### AutocompleteAttribute
-
-This setting provides a value to be used for the `autocomplete` attribute for newly created forms. By default the value is empty, but can be set to `on` or `off` to have that value applied as the attribute value used when rendering the form.
-
-## Package options configuration
-
-### IgnoreWorkFlowsOnEdit
-
-This configuration expects a `True` or `False` string value, or a comma-separated list of form names, and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the `AllowEditableFormSubmissions` configuration value. Defaults to `True`.
-
-### ExecuteWorkflowAsync
-
-This configuration key is *experimental* and will allow Workflows to be executed in an asynchronous manner. The value can be a `True` or `False` string value, or a comma-separated list of form names. Defaults to `False`.
-
-### AllowEditableFormSubmissions
-
-This configuration value expects a `true` or `false` value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to `true` it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. `?recordId=GUID` Replace `GUID` with the GUID of the form submission. Defaults to `false`.
-
-:::warning
-Enable this feature ONLY if you understand the security implications.
-:::
-
-## Security configuration
-
-### DisallowedFileUploadExtensions
-
-When using the File Upload field in a form, editors can choose which file extensions they want to accept. When an image is expected, they can for example specify that only `.jpg` or `.png` files are uploaded.
-
-There are certain file extensions that in almost all cases should never be allowed, which are held in this configuration value. This means that even if an editor has selected to allow all files, any files that match the extensions listed here will be blocked.
-
-By default, .NET related code files like `.config` and `.aspx` are included in this deny list. You can add or - if you are sure - remove values from this list to meet your needs.
-
-### EnableAntiForgeryToken
-
-This setting needs to be a `true` or `false` value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this option. Defaults to `true`.
-
-### SavePlainTextPasswords
-
-This setting needs to be a `true` or `false` value and controls whether password fields provided in forms will be saved to the database. Defaults to `false`.
-
-## Field type specific configuration
-
-### Date picker field type configuration
-
-#### DatePickerYearRange
-
-This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years.
-
-### reCAPTCHA v2 field type configuration
-
-#### PublicKey & PrivateKey
-
-Both of these configuration values are needed in order to use the "*Recaptcha2*" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here -
-
-Google has renamed these recently and the `Site Key` refers to `RecaptchaPublicKey` and `Secret Key` is to be used for `RecaptchaPrivateKey`
-
-### reCAPTCHA v3 field type configuration
-
-#### SiteKey & PrivateKey
-
-Both of these configuration values are needed in order to use the "*reCAPTCHA V3 with Score*" field type implementing ReCaptcha V3 from Google.
-
-You can obtain both of these values after signing up to create a ReCaptcha key here: .
-
----
-
-Prev: [Extending](../Extending/index.md) Next: [Magic Strings](../Magic-Strings/index.md)
diff --git a/Add-ons/UmbracoForms/Developer/Configuration/index.md b/Add-ons/UmbracoForms/Developer/Configuration/index.md
index 1ea8c7ff5ce..543d363573f 100644
--- a/Add-ons/UmbracoForms/Developer/Configuration/index.md
+++ b/Add-ons/UmbracoForms/Developer/Configuration/index.md
@@ -1,118 +1,215 @@
---
-versionFrom: 7.0.0
+versionFrom: 9.0.0
meta.Title: "Umbraco Forms configuration"
meta.Description: "In Umbraco Forms it's possible to customize the functionality with various configuration values."
+state: complete
+verified-against: beta-1
---
# Configuration
+
With Umbraco Forms it's possible to customize the functionality with various configuration values.
## Editing configuration values
-The configuration for Umbraco Forms can be changed by modifying the XML based config file found at `/App_Plugins/UmbracoForms/UmbracoForms.config`
-
-### UploadStorageDirectory
-This is *legacy and is no longer in use* - Forms that use an upload field will use the same IFileSystem as the [media section](../IFileSystem/#forms-containing-upload-fields)
-### IgnoreWorkFlowsOnEdit
-This configuration expects `True` or `False` and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the `AllowEditableFormSubmissions` configuration value.
+All configuration for Umbraco Forms is held in the `appSettings.json` file found at the root of your Umbraco website. If the configuration has been customized to use another source, then the same keys and values discussed in this article can be applied there.
+
+The convention for Umbraco configuration is to have package based options stored as a child structure below the `Umbraco` element, and as a sibling of `CMS`. Forms configuration follows this pattern, i.e.:
+
+```json
+{
+ ...
+ "Umbraco": {
+ "CMS": {
+ ...
+ },
+ "Forms": {
+ ...
+ }
+ }
+}
+```
+
+All configuration for Forms is optional. In other words, all values have defaults that will be applied if no configuration is available for a particular key.
+
+For illustration purposes, the following structure represents the full set of options for configuration of Forms, along with the default values. This will help when you need to provide a different setting to understand where it should be applied.
+
+```json
+ "Forms": {
+ "FormDesign": {
+ "DisableAutomaticAdditionOfDataConsentField": false,
+ "DisableDefaultWorkflow": false,
+ "MaxNumberOfColumnsInFormGroup": 12,
+ "DefaultTheme": "default",
+ "Defaults": {
+ "ManualApproval": false,
+ "DisableStylesheet": false,
+ "MarkFieldsIndicator": "NoIndicator",
+ "Indicator": "*",
+ "RequiredErrorMessage": "Please provide a value for {0}",
+ "InvalidErrorMessage": "Please provide a valid value for {0}",
+ "ShowValidationSummary": false,
+ "HideFieldValidationLabels": false,
+ "MessageOnSubmit": "Thank you",
+ "StoreRecordsLocally": true,
+ "AutocompleteAttribute": ""
+ }
+ },
+ "PackageOptions": {
+ "IgnoreWorkFlowsOnEdit": "True",
+ "ExecuteWorkflowAsync": "False",
+ "AllowEditableFormSubmisisons": false
+ },
+ "Security": {
+ "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx",
+ "EnableAntiForgeryToken": true,
+ "SavePlainTextPasswords": false
+ },
+ "FieldTypes": {
+ "DatePicker": {
+ "DatePickerYearRange": 10
+ },
+ "Recaptcha2": {
+ "PublicKey": "",
+ "PrivateKey": ""
+ },
+ "Recaptcha3": {
+ "SiteKey": "",
+ "PrivateKey": ""
+ }
+ }
+ }
+```
+
+## Form design configuration
-### ExecuteWorkflowAsync
-This configuration key is *experimental* and will allow Workflows to be executed in an async manner
-The value can be `True/False` or a list of form names that ignore workflows that are comma separated `form name,contact form`
+### DisableAutomaticAdditionOfDataConsentField
-### DisableFormCaching
-This configuration value expects a `True/False` value and can be used to toggle if Forms should be read from the JSON representation on disk or from the relevant Forms IFileSystem. Forms are cached for 10 minutes.
+This configuration value expects a `true` or `false` value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation. Defaults to `false`.
### DisableDefaultWorkflow
-This configuration value expects a `True/False` value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form.
-### DisableAutomaticAdditionOfDataConsentField
-This configuration value expects a `True/False` value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation.
+This configuration value expects a `true` or `false` value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form. Defaults to `false`.
-### AllowEditableFormSubmissions
-This configuration value expects a `True/False` value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to `True` it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. `?recordId=GUID` Replace `GUID` with the GUID of the form submission.
+### MaxNumberOfColumnsInFormGroup
-:::warning
-Enable this feature ONLY if you do understand the security implications.
-:::
+This setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12.
-### RecaptchaPublicKey & RecaptchaPrivateKey
-Both of these configuration values are needed in order to use the "*Recaptcha2*" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here - https://www.google.com/recaptcha/admin
+### DefaultTheme
+This setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme.
-Google has renamed these recently and the `Site Key` refers to `RecaptchaPublicKey` and `Secret Key` is to be used for `RecaptchaPrivateKey`
+### Form default settings configuration
-### RecaptchaV3SiteKey & RecaptchaV3PrivateKey
-Both of these configuration values are needed in order to use the "*reCAPTCHA V3 with Score*" field type implementing ReCaptcha V3 from Google. This field type is available in Umbraco Forms from v8.7+.
+The following configured values are applied to all forms as they are created. They can then be amended on a per-form basis via the Umbraco backoffice.
-You can obtain both of these values after signing up to create a ReCaptcha key here: https://www.google.com/recaptcha/admin.
+#### ManualApproval
-### DatePickerYearRange
-This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years.
+This setting needs to be a `true` or `false` value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign. Defaults to `false`.
-### EnableAntiForgeryToken
-This setting needs to be a `True` or `False` value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this and set this to `True`. Due to older versions of Umbraco Forms not containing this, it has become an optional config setting and due to upgrade reasons we do not automatically set this to `True` for you.
+#### DisableStylesheet
+
+This setting needs to be a `true` or `false` value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet. Defaults to `false`.
+
+#### MarkFieldsIndicator
+
+This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields:
+
+* `NoIndicator` (default)
+* `MarkMandatoryFields`
+* `MarkOptionalFields`
+
+#### Indicator
+
+This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk `*`.
+
+#### RequiredErrorMessage
+
+This allows you to configure the required error validation message. By default this is `Please provide a value for {0}` where the `{0}` is used to replace the name of the field that is required.
+
+#### InvalidErrorMessage
+
+This allows you to configure the invalid error validation message. By default this is `Please provide a valid value for {0}` where the `{0}` is used to replace the name of the field that is invalid.
+
+#### ShowValidationSummary
+
+This setting needs to be a `true` or `false` value and will allow you to toggle if the form will display all form validation error messages in a validation summary together. Defaults to `false`.
+
+#### HideFieldValidationLabels
-If you do set this to `True` then you need to add `@Html.AntiForgeryToken()` to your forms. The default template for Forms can be found in `~/Views/Partials/Forms/Form.cshtml` and should have `@Html.AntiForgeryToken()` in the `@using (Html.BeginUmbracoForm [...]` block.
+This setting needs to be a `true` or `false` value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid. Defaults to `false`.
-### StoreUmbracoFormsInDb
-This setting needs to be set to `True` if you want your Forms data to be stored in the database instead of the .json files in the `App_Data/UmbracoForms` directory in the file system.
+#### MessageOnSubmit
-For more information on this, read the [Forms in the Database](../Forms-in-the-Database) article.
+This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node. Defaults to `Thank you`.
-### UseLegacyPageService
-In Umbraco Forms 8.7 an update was made to improve the performance of the service responsible for retrieving the content of the Umbraco page where a form is hosted. This service is used to populate the string placeholders - or "magic strings" - with the values of properties from the page.
+#### StoreRecordsLocally
-By setting the value of the `UseLegacyPageService` to `True` the old service can be reinstated.
+This setting needs to be a `True` or `False` value and will allow you to toggle if form submission data should be stored in the Umbraco Forms database tables. By default this is set to `True`.
+
+#### AutocompleteAttribute
+
+This setting provides a value to be used for the `autocomplete` attribute for newly created forms. By default the value is empty, but can be set to `on` or `off` to have that value applied as the attribute value used when rendering the form.
+
+## Package options configuration
+
+### IgnoreWorkFlowsOnEdit
+
+This configuration expects a `True` or `False` string value, or a comma-separated list of form names, and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the `AllowEditableFormSubmissions` configuration value. Defaults to `True`.
+
+### ExecuteWorkflowAsync
+
+This configuration key is *experimental* and will allow Workflows to be executed in an asynchronous manner. The value can be a `True` or `False` string value, or a comma-separated list of form names. Defaults to `False`.
+
+### AllowEditableFormSubmissions
+
+This configuration value expects a `true` or `false` value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to `true` it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. `?recordId=GUID` Replace `GUID` with the GUID of the form submission. Defaults to `false`.
+
+:::warning
+Enable this feature ONLY if you understand the security implications.
+:::
+
+## Security configuration
### DisallowedFileUploadExtensions
+
When using the File Upload field in a form, editors can choose which file extensions they want to accept. When an image is expected, they can for example specify that only `.jpg` or `.png` files are uploaded.
There are certain file extensions that in almost all cases should never be allowed, which are held in this configuration value. This means that even if an editor has selected to allow all files, any files that match the extensions listed here will be blocked.
By default, .NET related code files like `.config` and `.aspx` are included in this deny list. You can add or - if you are sure - remove values from this list to meet your needs.
-### MaxNumberOfColumnsInFormGroup
-Added in 8.7.0, this setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12.
+### EnableAntiForgeryToken
-### DefaultTheme
-Added in 8.8.0, this setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme.
+This setting needs to be a `true` or `false` value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this option. Defaults to `true`.
-## Default Settings
-There are several configuration keys that start with `Default`. This allows you to configure the values for when a new form is created.
+### SavePlainTextPasswords
-### DefaultManualApproval
-This setting needs to be a `True` or `False` value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign.
+This setting needs to be a `true` or `false` value and controls whether password fields provided in forms will be saved to the database. Defaults to `false`.
-### DefaultDisableStylesheet
-This setting needs to be a `True` or `False` value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet.
+## Field type specific configuration
-### DefaultMarkFieldsIndicator
-This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields
-* `NoIndicator`
-* `MarkMandatoryFields`
-* `MarkOptionalFields`
+### Date picker field type configuration
-### DefaultIndicator
-This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk `*`
+#### DatePickerYearRange
-### DefaultRequiredErrorMessage
-This allows you to configure the required error validation message. By default this is `Please provide a value for {0}` where the `{0}` is used to replace the name of the field that is required.
+This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years.
-### DefaultInvalidErrorMessage
-This allows you to configure the invalid error validation message. By default this is `Please provide a valid value for {0}` where the `{0}` is used to replace the name of the field that is invalid.
+### reCAPTCHA v2 field type configuration
-### DefaultShowValidationSummary
-This setting needs to be a `True` or `False` value and will allow you to toggle if the form will display all form validation error messages in a validation summary together.
+#### PublicKey & PrivateKey
-### DefaultHideFieldValidationLabels
-This setting needs to be a `True` or `False` value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid.
+Both of these configuration values are needed in order to use the "*Recaptcha2*" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here -
+
+Google has renamed these recently and the `Site Key` refers to `RecaptchaPublicKey` and `Secret Key` is to be used for `RecaptchaPrivateKey`
-### DefaultMessageOnSubmit
-This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node.
+### reCAPTCHA v3 field type configuration
-### DefaultStoreRecordsLocally
-This setting needs to be a `True` or `False` value and will allow you to toggle if form submission data will be stored in the Umbraco Forms database tables. By default this is set to `True`.
+#### SiteKey & PrivateKey
-### DefaultAutocompleteAttribute
+Both of these configuration values are needed in order to use the "*reCAPTCHA V3 with Score*" field type implementing ReCaptcha V3 from Google.
+
+You can obtain both of these values after signing up to create a ReCaptcha key here: .
+
+---
-Added in 8.8.0, this setting provides a value to be used for the `autocomplete` attribute for newly created forms. By default the value is empty, but can be set to `on` or `off` to have that value applied as the attribute value used when rendering the form.
+Prev: [Extending](../Extending/index.md) Next: [Magic Strings](../Magic-Strings/index.md)
diff --git a/Add-ons/UmbracoForms/Developer/Custom-Markup/index.md b/Add-ons/UmbracoForms/Developer/Custom-Markup/index.md
index 373c733e4e1..63b9c8d6d1c 100644
--- a/Add-ons/UmbracoForms/Developer/Custom-Markup/index.md
+++ b/Add-ons/UmbracoForms/Developer/Custom-Markup/index.md
@@ -79,4 +79,4 @@ You can also overwrite views for one or more fieldtypes by adding the views to t
---
-Prev: [Themes](../Themes/index.md) Next: [Email Templates](../Email-Templates/index-v9.md)
+Prev: [Themes](../Themes/index.md) Next: [Email Templates](../Email-Templates/index.md)
diff --git a/Add-ons/UmbracoForms/Developer/Email-Templates/index-v9.md b/Add-ons/UmbracoForms/Developer/Email-Templates/index-v7.md
similarity index 84%
rename from Add-ons/UmbracoForms/Developer/Email-Templates/index-v9.md
rename to Add-ons/UmbracoForms/Developer/Email-Templates/index-v7.md
index 55b9455997a..8057231e083 100644
--- a/Add-ons/UmbracoForms/Developer/Email-Templates/index-v9.md
+++ b/Add-ons/UmbracoForms/Developer/Email-Templates/index-v7.md
@@ -1,5 +1,6 @@
---
-versionFrom: 9.0.0
+versionFrom: 7.0.0
+versionTo: 8.0.0
meta.Title: "Umbraco Forms Email Templates"
meta.Description: "Creating an email template for Umbraco Forms."
---
@@ -8,6 +9,8 @@ meta.Description: "Creating an email template for Umbraco Forms."
From version 6+, we now include a new Workflow **Send email with template (Razor)** that allows you to pick a Razor view file that can be used to send out a *pretty HTML email* for Form submissions.
+We have included an example email template below to look and understand how it works. The email template can be found at `~/Views/Partials/Forms/Emails/` folder.
+
## Creating an Email Template
If you wish to have one or more templates to choose from the **Send email with template (Razor)**, you will need to place all email templates into the `~/Views/Partials/Forms/Emails/` folder.
@@ -15,15 +18,17 @@ If you wish to have one or more templates to choose from the **Send email with t
The Razor view must inherit from FormsHtmlModel:
```csharp
-@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
+@inherits UmbracoViewPage
```
You now have a model that contains your Form fields which can be used in your email HTML markup, along with the UmbracoHelper methods such as `Umbraco.TypedContent` and `Umbraco.TypedMedia` etc.
-Below is an example of an email template from the `~/Views/Partials/Forms/Emails/` folder:
+Below is an example of an email template:
+
+### For Version 8.0.0+
```csharp
-@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
+@inherits UmbracoViewPage
@{
//This is an example email template where you can use Razor Views to send HTML emails
@@ -40,7 +45,7 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails
//@foreach(var color in Model.DynamicFields.checkboxField
//Images need to be absolute - so fetching domain to prefix with images
- var siteDomain = Context.Request.Scheme + "://" + Context.Request.Host;
+ var siteDomain = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
var assetUrl = siteDomain + "/App_plugins/UmbracoForms/Assets/Email-Example";
}
@@ -74,13 +79,13 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails
line-height: inherit !important;
}
- /* MOBILE STYLES */
- @@media screen and (max-width:600px){
- h1 {
- font-size: 32px !important;
- line-height: 32px !important;
- }
- }
+ /* MOBILE STYLES */
+ @@media screen and (max-width:600px){
+ h1 {
+ font-size: 32px !important;
+ line-height: 32px !important;
+ }
+ }
/* ANDROID CENTER FIX */
div[style*="margin: 16px 0;"] { margin: 0 !important; }
@@ -172,7 +177,7 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails
-
+
@foreach (var field in Model.Fields)
{
@field.Name
@@ -247,6 +252,60 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails
```
----
+### For Version 7+
+
+```csharp
+@inherits UmbracoViewPage
+
+@{
+ // This is an example email template where you can use Razor Views to send HTML emails
+
+ // You can use Umbraco.TypedContent & Umbraco.TypedMedia etc to use Images & content from your site
+ // directly in your email templates too
-Prev: [Custom Markup](../Custom-Markup/index.md) Next: [Working with Record Data](../Working-With-Data/index-v9.md)
+ // Strongly Typed
+ // @Model.GetValue("aliasFormField")
+ // @foreach (var color in Model.GetValues("checkboxField")){}
+
+}
+
+
Explicitly Named Fields
+
Name:
+@Model.GetValue("name")
+
+
Favourite Colors
+
+ @foreach (var color in Model.GetValues("favColors")) {
+
@color
+ }
+
+
+
+
+
Generic/reusable template
+@foreach (var field in Model.Fields)
+{
+
@field.Name
+
+ switch (field.FieldType)
+ {
+ case "FieldType.FileUpload.cshtml":
+ @field.GetValue()
+ break;
+
+ case "FieldType.DatePicker.cshtml":
+ @(Convert.ToDateTime(field.GetValue()).ToString("f"))
+ break;
+
+ case "FieldType.CheckboxList.cshtml":
+ foreach (var color in field.GetValues())
+ {
+ @color
+ }
+ break;
+ default:
+ @field.GetValue()
+ break;
+ }
+}
+```
diff --git a/Add-ons/UmbracoForms/Developer/Email-Templates/index.md b/Add-ons/UmbracoForms/Developer/Email-Templates/index.md
index 8057231e083..7423e5beea9 100644
--- a/Add-ons/UmbracoForms/Developer/Email-Templates/index.md
+++ b/Add-ons/UmbracoForms/Developer/Email-Templates/index.md
@@ -1,6 +1,5 @@
---
-versionFrom: 7.0.0
-versionTo: 8.0.0
+versionFrom: 9.0.0
meta.Title: "Umbraco Forms Email Templates"
meta.Description: "Creating an email template for Umbraco Forms."
---
@@ -9,8 +8,6 @@ meta.Description: "Creating an email template for Umbraco Forms."
From version 6+, we now include a new Workflow **Send email with template (Razor)** that allows you to pick a Razor view file that can be used to send out a *pretty HTML email* for Form submissions.
-We have included an example email template below to look and understand how it works. The email template can be found at `~/Views/Partials/Forms/Emails/` folder.
-
## Creating an Email Template
If you wish to have one or more templates to choose from the **Send email with template (Razor)**, you will need to place all email templates into the `~/Views/Partials/Forms/Emails/` folder.
@@ -18,17 +15,15 @@ If you wish to have one or more templates to choose from the **Send email with t
The Razor view must inherit from FormsHtmlModel:
```csharp
-@inherits UmbracoViewPage
+@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
```
You now have a model that contains your Form fields which can be used in your email HTML markup, along with the UmbracoHelper methods such as `Umbraco.TypedContent` and `Umbraco.TypedMedia` etc.
-Below is an example of an email template:
-
-### For Version 8.0.0+
+Below is an example of an email template from the `~/Views/Partials/Forms/Emails/` folder:
```csharp
-@inherits UmbracoViewPage
+@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@{
//This is an example email template where you can use Razor Views to send HTML emails
@@ -45,7 +40,7 @@ Below is an example of an email template:
//@foreach(var color in Model.DynamicFields.checkboxField
//Images need to be absolute - so fetching domain to prefix with images
- var siteDomain = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
+ var siteDomain = Context.Request.Scheme + "://" + Context.Request.Host;
var assetUrl = siteDomain + "/App_plugins/UmbracoForms/Assets/Email-Example";
}
@@ -79,13 +74,13 @@ Below is an example of an email template:
line-height: inherit !important;
}
- /* MOBILE STYLES */
- @@media screen and (max-width:600px){
- h1 {
- font-size: 32px !important;
- line-height: 32px !important;
- }
- }
+ /* MOBILE STYLES */
+ @@media screen and (max-width:600px){
+ h1 {
+ font-size: 32px !important;
+ line-height: 32px !important;
+ }
+ }
/* ANDROID CENTER FIX */
div[style*="margin: 16px 0;"] { margin: 0 !important; }
@@ -177,7 +172,7 @@ Below is an example of an email template:
-
+
@foreach (var field in Model.Fields)
{
@field.Name
@@ -252,60 +247,6 @@ Below is an example of an email template:
```
-### For Version 7+
-
-```csharp
-@inherits UmbracoViewPage
-
-@{
- // This is an example email template where you can use Razor Views to send HTML emails
-
- // You can use Umbraco.TypedContent & Umbraco.TypedMedia etc to use Images & content from your site
- // directly in your email templates too
-
- // Strongly Typed
- // @Model.GetValue("aliasFormField")
- // @foreach (var color in Model.GetValues("checkboxField")){}
-
-}
-
-
Explicitly Named Fields
-
Name:
-@Model.GetValue("name")
+---
-
Favourite Colors
-
- @foreach (var color in Model.GetValues("favColors")) {
-
@color
- }
-
-
-
-
-
Generic/reusable template
-@foreach (var field in Model.Fields)
-{
-
@field.Name
-
- switch (field.FieldType)
- {
- case "FieldType.FileUpload.cshtml":
- @field.GetValue()
- break;
-
- case "FieldType.DatePicker.cshtml":
- @(Convert.ToDateTime(field.GetValue()).ToString("f"))
- break;
-
- case "FieldType.CheckboxList.cshtml":
- foreach (var color in field.GetValues())
- {
- @color
- }
- break;
- default:
- @field.GetValue()
- break;
- }
-}
-```
+Prev: [Custom Markup](../Custom-Markup/index.md) Next: [Working with Record Data](../Working-With-Data/index.md)
diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v7.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v7.md
new file mode 100644
index 00000000000..c22f6830325
--- /dev/null
+++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v7.md
@@ -0,0 +1,192 @@
+---
+versionFrom: 7.0.0
+meta.Title: "Adding a Export type to Umbraco Forms"
+---
+
+# Adding a Export type to Umbraco Forms
+*This builds on the "[adding a type to the provider model](Adding-a-Type.md)" chapter and applies to Umbraco Forms version 4.4.1 and higher*
+
+Add a new class to your project and have it inherit from `Umbraco.Forms.Core.ExportType` and you have two options when implementing the class.
+
+## Basic Example
+When implementing the method `public override string ExportRecords(RecordExportFilter filter)` in your export provider class. You need to return the final string you wish to write to a file. Such as .txt file or .csv and you can perform your logic to build up a comma separated string for a CSV file in the `ExportRecords` method.
+
+In the constructor of your provider, note that you will need a further two properties, `FileExtension` and `Icon`. The FileExtension property is the file extension such as `zip`, `txt` or `csv` of the file you will be generating & serving from the file system as the export file.
+
+In this example below we will create a single HTML file which takes all the submissions/entries to be displayed as a HTML report. We will do this in conjunction with a Razor partial view to help build up our HTML and thus merge it with the form submission data to generate a string of HTML.
+
+### Provider Class
+
+```csharp
+public class ExportToHtmlReport : ExportType
+{
+ public ExportToHtmlReport()
+ {
+ this.Name = "Export as HTML";
+ this.Description = "Export entries as a single HTML report";
+ this.Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2");
+ this.FileExtension = "html";
+ this.Icon = "icon-article";
+ }
+
+ ///
+ /// We implement this method from the interface
+ /// As this method is called from ExportToFile & is expecting the file contents as a string to be written as a stream to a file
+ ///
+ public override string ExportRecords(RecordExportFilter filter)
+ {
+ var view = "~/Views/Partials/Forms/Export/html-report.cshtml";
+ var model = BusinessLogic.FormRecordSearcher.QueryDataBase(filter);
+ return Helpers.ViewHelper.RenderPartialViewToString(view, model);
+ }
+}
+```
+
+### Razor Partial View
+
+```csharp
+@model Umbraco.Forms.Web.Models.Backoffice.EntrySearchResultCollection
+
+@{
+ var submissions = Model.Results.ToList();
+ var schemaItems = Model.schema.ToList();
+}
+
+
Form Submissions
+
+@foreach (var submission in submissions)
+{
+ var values = submission.Fields.ToList();
+
+ for (int i = 0; i < schemaItems.Count; i++)
+ {
+ @schemaItems[i].Name @values[i]
+ }
+
+
+}
+```
+
+## Advanced Example
+This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to excel file export provider we ship in Umbraco Forms. With this we can use a library to create the excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file.
+
+In this example we will create a collection of text files, one for each submission which is then zipped up into a single file and served as the export file.
+
+```csharp
+public class ExportToTextFile : ExportType
+{
+ public ExportToTextFile()
+ {
+ this.Name = "Export as text files";
+ this.Description = "Export entries as text files inside a zip file";
+ this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB");
+ this.FileExtension = "zip";
+ this.Icon = "icon-zip";
+ }
+
+ ///
+ /// We do not implement this method from the interface
+ /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file
+ /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps
+ ///
+
+ public override string ExportRecords(RecordExportFilter filter)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// This gives us greater control of the export process
+ ///
+ ///
+ /// This filter contains the date range & other search parameters to limit the entries we are exporting
+ ///
+ ///
+ /// The filepath that the export file is expecting to be served from
+ /// So ensure that the zip of text files is saved at this location
+ ///
+ /// The final file path to serve up as the export - this is unlikely to change through the export logic
+ public override string ExportToFile(RecordExportFilter filter, string filepath)
+ {
+ // Before Save - Check Path, Directory & Previous File export does not exist
+ string pathToSaveZipFile = filepath;
+
+ // Check our path does not contain \\
+ // If not, use the filePath
+ if (filepath.Contains('\\') == false)
+ {
+ pathToSaveZipFile = IOHelper.MapPath(filepath);
+ }
+
+ // Get the directory (strip out \\ if it exists)
+ var dir = filepath.Substring(0, filepath.LastIndexOf('\\'));
+ var tempFileDir = Path.Combine(dir, "text-files");
+
+
+ // If the path does not end with our file extension, ensure it's added
+ if (pathToSaveZipFile.EndsWith("." + FileExtension) == false)
+ {
+ pathToSaveZipFile += "." + FileExtension;
+ }
+
+ // Check that the directory where we will save the ZIP file temporarily exists
+ // If not just create it
+ if (Directory.Exists(tempFileDir) == false)
+ {
+ Directory.CreateDirectory(tempFileDir);
+ }
+
+ // Check if the zip file exists already - if so delete it, as we have a new update
+ if (File.Exists(pathToSaveZipFile))
+ {
+ File.Delete(pathToSaveZipFile);
+ }
+
+
+ // Query the DB for submissions to export based on the filter
+ var submissions = FormRecordSearcher.QueryDataBase(filter);
+
+ // Get the schema objects to a list so we can get items using position index
+ var schemaItems = submissions.schema.ToList();
+
+ // We will use this to store our contents of our file to save as a text file
+ var fileContents = string.Empty;
+
+ // For each submission we have build up a string to save to a text file
+ foreach (var submission in submissions.Results)
+ {
+ // The submitted data for the form submission
+ var submissionData = submission.Fields.ToList();
+
+ // For loop to match the schema position to the submission data
+ for (int i = 0; i < schemaItems.Count; i++)
+ {
+ // Concat a string of the name of the field & its stored data
+ fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine;
+ }
+
+ // Now save the contents to a text file
+ // Base it on the format of the record submission unique id
+ var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt");
+ File.WriteAllText(textFileName, fileContents);
+
+ // Reset fileContents to be empty again
+ fileContents = string.Empty;
+ }
+
+ // Now we have a temp folder full of text files
+ // Generate a zip file containing them & save that
+ ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile);
+
+ // Tidy up after ourselves & delete the temp folder of text files
+ if (Directory.Exists(tempFileDir))
+ {
+ Directory.Delete(tempFileDir, true);
+ }
+
+ // Return the path where we saved the zip file containing the text files
+ return pathToSaveZipFile;
+ }
+
+}
+```
diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v9.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v9.md
deleted file mode 100644
index 26ef7501f59..00000000000
--- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype-v9.md
+++ /dev/null
@@ -1,219 +0,0 @@
----
-versionFrom: 9.0.0
-meta.Title: "Adding a Export type to Umbraco Forms"
-state: complete
-verified-against: beta001
----
-
-# Adding a Export type to Umbraco Forms
-*This builds on the "[adding a type to the provider model](Adding-a-Type-v9.md)" chapter and applies to Umbraco Forms version 4.4.1 and higher*
-
-Add a new class to your project and have it inherit from `Umbraco.Forms.Core.ExportType` and you have two options when implementing the class.
-
-## Basic Example
-When implementing the method `public override string ExportRecords(RecordExportFilter filter)` in your export provider class. You need to return the final string you wish to write to a file. Such as .txt file or .csv and you can perform your logic to build up a comma separated string for a CSV file in the `ExportRecords` method.
-
-In the constructor of your provider, note that you will need a further two properties, `FileExtension` and `Icon`. The FileExtension property is the file extension such as `zip`, `txt` or `csv` of the file you will be generating & serving from the file system as the export file.
-
-In this example below we will create a single HTML file which takes all the submissions/entries to be displayed as a HTML report. We will do this in conjunction with a Razor partial view to help build up our HTML and thus merge it with the form submission data to generate a string of HTML.
-
-### Provider Class
-
-```csharp
-using System;
-using Umbraco.Cms.Core.Hosting;
-using Umbraco.Forms.Core;
-using Umbraco.Forms.Core.Models;
-using Umbraco.Forms.Core.Searchers;
-using Umbraco.Forms.Web.Helpers;
-
-namespace MyFormsExtensions
-{
- public class ExportToHtmlReport : ExportType
- {
- private readonly IFormRecordSearcher _formRecordSearcher;
-
- public ExportToHtmlReport(
- IHostingEnvironment hostingEnvironment,
- IFormRecordSearcher formRecordSearcher)
- : base(hostingEnvironment)
- {
- _formRecordSearcher = formRecordSearcher;
-
- this.Name = "Export as HTML";
- this.Description = "Export entries as a single HTML report";
- this.Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2");
- this.FileExtension = "html";
- this.Icon = "icon-article"; }
-
- public override string ExportRecords(RecordExportFilter filter)
- {
- var view = "~/Views/Partials/Forms/Export/html-report.cshtml";
- EntrySearchResultCollection model = _formRecordSearcher.QueryDataBase(filter);
- return ViewHelper.RenderPartialViewToString(view, model);
- }
- }
-}
-```
-
-### Razor Partial View
-
-```csharp
-@model Umbraco.Forms.Web.Models.Backoffice.EntrySearchResultCollection
-
-@{
- var submissions = Model.Results.ToList();
- var schemaItems = Model.schema.ToList();
-}
-
-
Form Submissions
-
-@foreach (var submission in submissions)
-{
- var values = submission.Fields.ToList();
-
- for (int i = 0; i < schemaItems.Count; i++)
- {
- @schemaItems[i].Name @values[i]
- }
-
-
-}
-```
-
-## Advanced Example
-This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to Excel file export provider we ship in Umbraco Forms. With this we can use a library to create the Excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file.
-
-In this example we will create a collection of text files, one for each submission which is then zipped up into a single file and served as the export file.
-
-```csharp
-using System;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using Umbraco.Cms.Core.Hosting;
-using Umbraco.Forms.Core;
-using Umbraco.Forms.Core.Models;
-using Umbraco.Forms.Core.Searchers;
-
-namespace MyFormsExtensions
-{
- public class ExportToTextFiles : ExportType
- {
- private readonly IFormRecordSearcher _formRecordSearcher;
-
- public ExportToTextFiles(
- IHostingEnvironment hostingEnvironment,
- IFormRecordSearcher formRecordSearcher)
- : base(hostingEnvironment)
- {
- _formRecordSearcher = formRecordSearcher;
-
- this.Name = "Export as text files";
- this.Description = "Export entries as text files inside a zip file";
- this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB");
- this.FileExtension = "zip";
- this.Icon = "icon-zip";
- }
-
- ///
- /// We do not implement this method from the interface
- /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file
- /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps
- ///
- public override string ExportRecords(RecordExportFilter filter) => throw new NotImplementedException();
-
- ///
- /// This gives us greater control of the export process
- ///
- ///
- /// This filter contains the date range & other search parameters to limit the entries we are exporting
- ///
- ///
- /// The filepath that the export file is expecting to be served from
- /// So ensure that the zip of text files is saved at this location
- ///
- /// The final file path to serve up as the export - this is unlikely to change through the export logic
- public override string ExportToFile(RecordExportFilter filter, string filepath)
- {
- // Before Save - Check Path, Directory & Previous File export does not exist
- string pathToSaveZipFile = filepath;
-
- // Check our path does not contain \\
- // If not, use the filePath
- if (filepath.Contains('\\') == false)
- {
- pathToSaveZipFile = HostingEnvironment.MapPathContentRoot(filepath);
- }
-
- // Get the directory (strip out \\ if it exists)
- var dir = filepath.Substring(0, filepath.LastIndexOf('\\'));
- var tempFileDir = Path.Combine(dir, "text-files");
-
-
- // If the path does not end with our file extension, ensure it's added
- if (pathToSaveZipFile.EndsWith("." + FileExtension) == false)
- {
- pathToSaveZipFile += "." + FileExtension;
- }
-
- // Check that the directory where we will save the ZIP file temporarily exists
- // If not just create it
- if (Directory.Exists(tempFileDir) == false)
- {
- Directory.CreateDirectory(tempFileDir);
- }
-
- // Check if the zip file exists already - if so delete it, as we have a new update
- if (File.Exists(pathToSaveZipFile))
- {
- File.Delete(pathToSaveZipFile);
- }
-
- // Query the DB for submissions to export based on the filter
- EntrySearchResultCollection submissions = _formRecordSearcher.QueryDataBase(filter);
-
- // Get the schema objects to a list so we can get items using position index
- var schemaItems = submissions.schema.ToList();
-
- // We will use this to store our contents of our file to save as a text file
- var fileContents = string.Empty;
-
- // For each submission we have build up a string to save to a text file
- foreach (EntrySearchResult submission in submissions.Results)
- {
- // The submitted data for the form submission
- var submissionData = submission.Fields.ToList();
-
- // For loop to match the schema position to the submission data
- for (int i = 0; i < schemaItems.Count; i++)
- {
- // Concat a string of the name of the field & its stored data
- fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine;
- }
-
- // Now save the contents to a text file
- // Base it on the format of the record submission unique id
- var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt");
- File.WriteAllText(textFileName, fileContents);
-
- // Reset fileContents to be empty again
- fileContents = string.Empty;
- }
-
- // Now we have a temp folder full of text files
- // Generate a zip file containing them & save that
- ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile);
-
- // Tidy up after ourselves & delete the temp folder of text files
- if (Directory.Exists(tempFileDir))
- {
- Directory.Delete(tempFileDir, true);
- }
-
- // Return the path where we saved the zip file containing the text files
- return pathToSaveZipFile;
- }
- }
-}
-```
diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype.md
index c22f6830325..882545dccf8 100644
--- a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype.md
+++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Exporttype.md
@@ -1,6 +1,8 @@
---
-versionFrom: 7.0.0
+versionFrom: 9.0.0
meta.Title: "Adding a Export type to Umbraco Forms"
+state: complete
+verified-against: beta001
---
# Adding a Export type to Umbraco Forms
@@ -18,26 +20,38 @@ In this example below we will create a single HTML file which takes all the subm
### Provider Class
```csharp
-public class ExportToHtmlReport : ExportType
+using System;
+using Umbraco.Cms.Core.Hosting;
+using Umbraco.Forms.Core;
+using Umbraco.Forms.Core.Models;
+using Umbraco.Forms.Core.Searchers;
+using Umbraco.Forms.Web.Helpers;
+
+namespace MyFormsExtensions
{
- public ExportToHtmlReport()
+ public class ExportToHtmlReport : ExportType
{
- this.Name = "Export as HTML";
- this.Description = "Export entries as a single HTML report";
- this.Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2");
- this.FileExtension = "html";
- this.Icon = "icon-article";
- }
+ private readonly IFormRecordSearcher _formRecordSearcher;
- ///
- /// We implement this method from the interface
- /// As this method is called from ExportToFile & is expecting the file contents as a string to be written as a stream to a file
- ///
- public override string ExportRecords(RecordExportFilter filter)
- {
- var view = "~/Views/Partials/Forms/Export/html-report.cshtml";
- var model = BusinessLogic.FormRecordSearcher.QueryDataBase(filter);
- return Helpers.ViewHelper.RenderPartialViewToString(view, model);
+ public ExportToHtmlReport(
+ IHostingEnvironment hostingEnvironment,
+ IFormRecordSearcher formRecordSearcher)
+ : base(hostingEnvironment)
+ {
+ _formRecordSearcher = formRecordSearcher;
+
+ this.Name = "Export as HTML";
+ this.Description = "Export entries as a single HTML report";
+ this.Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2");
+ this.FileExtension = "html";
+ this.Icon = "icon-article"; }
+
+ public override string ExportRecords(RecordExportFilter filter)
+ {
+ var view = "~/Views/Partials/Forms/Export/html-report.cshtml";
+ EntrySearchResultCollection model = _formRecordSearcher.QueryDataBase(filter);
+ return ViewHelper.RenderPartialViewToString(view, model);
+ }
}
}
```
@@ -68,125 +82,138 @@ public class ExportToHtmlReport : ExportType
```
## Advanced Example
-This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to excel file export provider we ship in Umbraco Forms. With this we can use a library to create the excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file.
+This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to Excel file export provider we ship in Umbraco Forms. With this we can use a library to create the Excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file.
In this example we will create a collection of text files, one for each submission which is then zipped up into a single file and served as the export file.
```csharp
-public class ExportToTextFile : ExportType
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using Umbraco.Cms.Core.Hosting;
+using Umbraco.Forms.Core;
+using Umbraco.Forms.Core.Models;
+using Umbraco.Forms.Core.Searchers;
+
+namespace MyFormsExtensions
{
- public ExportToTextFile()
+ public class ExportToTextFiles : ExportType
{
- this.Name = "Export as text files";
- this.Description = "Export entries as text files inside a zip file";
- this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB");
- this.FileExtension = "zip";
- this.Icon = "icon-zip";
- }
+ private readonly IFormRecordSearcher _formRecordSearcher;
- ///
- /// We do not implement this method from the interface
- /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file
- /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps
- ///
-
- public override string ExportRecords(RecordExportFilter filter)
- {
- throw new NotImplementedException();
- }
-
- ///
- /// This gives us greater control of the export process
- ///
- ///
- /// This filter contains the date range & other search parameters to limit the entries we are exporting
- ///
- ///
- /// The filepath that the export file is expecting to be served from
- /// So ensure that the zip of text files is saved at this location
- ///
- /// The final file path to serve up as the export - this is unlikely to change through the export logic
- public override string ExportToFile(RecordExportFilter filter, string filepath)
- {
- // Before Save - Check Path, Directory & Previous File export does not exist
- string pathToSaveZipFile = filepath;
-
- // Check our path does not contain \\
- // If not, use the filePath
- if (filepath.Contains('\\') == false)
+ public ExportToTextFiles(
+ IHostingEnvironment hostingEnvironment,
+ IFormRecordSearcher formRecordSearcher)
+ : base(hostingEnvironment)
{
- pathToSaveZipFile = IOHelper.MapPath(filepath);
+ _formRecordSearcher = formRecordSearcher;
+
+ this.Name = "Export as text files";
+ this.Description = "Export entries as text files inside a zip file";
+ this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB");
+ this.FileExtension = "zip";
+ this.Icon = "icon-zip";
}
- // Get the directory (strip out \\ if it exists)
- var dir = filepath.Substring(0, filepath.LastIndexOf('\\'));
- var tempFileDir = Path.Combine(dir, "text-files");
+ ///
+ /// We do not implement this method from the interface
+ /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file
+ /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps
+ ///
+ public override string ExportRecords(RecordExportFilter filter) => throw new NotImplementedException();
+
+ ///
+ /// This gives us greater control of the export process
+ ///
+ ///
+ /// This filter contains the date range & other search parameters to limit the entries we are exporting
+ ///
+ ///
+ /// The filepath that the export file is expecting to be served from
+ /// So ensure that the zip of text files is saved at this location
+ ///
+ /// The final file path to serve up as the export - this is unlikely to change through the export logic
+ public override string ExportToFile(RecordExportFilter filter, string filepath)
+ {
+ // Before Save - Check Path, Directory & Previous File export does not exist
+ string pathToSaveZipFile = filepath;
+ // Check our path does not contain \\
+ // If not, use the filePath
+ if (filepath.Contains('\\') == false)
+ {
+ pathToSaveZipFile = HostingEnvironment.MapPathContentRoot(filepath);
+ }
- // If the path does not end with our file extension, ensure it's added
- if (pathToSaveZipFile.EndsWith("." + FileExtension) == false)
- {
- pathToSaveZipFile += "." + FileExtension;
- }
+ // Get the directory (strip out \\ if it exists)
+ var dir = filepath.Substring(0, filepath.LastIndexOf('\\'));
+ var tempFileDir = Path.Combine(dir, "text-files");
- // Check that the directory where we will save the ZIP file temporarily exists
- // If not just create it
- if (Directory.Exists(tempFileDir) == false)
- {
- Directory.CreateDirectory(tempFileDir);
- }
- // Check if the zip file exists already - if so delete it, as we have a new update
- if (File.Exists(pathToSaveZipFile))
- {
- File.Delete(pathToSaveZipFile);
- }
+ // If the path does not end with our file extension, ensure it's added
+ if (pathToSaveZipFile.EndsWith("." + FileExtension) == false)
+ {
+ pathToSaveZipFile += "." + FileExtension;
+ }
+ // Check that the directory where we will save the ZIP file temporarily exists
+ // If not just create it
+ if (Directory.Exists(tempFileDir) == false)
+ {
+ Directory.CreateDirectory(tempFileDir);
+ }
- // Query the DB for submissions to export based on the filter
- var submissions = FormRecordSearcher.QueryDataBase(filter);
+ // Check if the zip file exists already - if so delete it, as we have a new update
+ if (File.Exists(pathToSaveZipFile))
+ {
+ File.Delete(pathToSaveZipFile);
+ }
- // Get the schema objects to a list so we can get items using position index
- var schemaItems = submissions.schema.ToList();
+ // Query the DB for submissions to export based on the filter
+ EntrySearchResultCollection submissions = _formRecordSearcher.QueryDataBase(filter);
- // We will use this to store our contents of our file to save as a text file
- var fileContents = string.Empty;
+ // Get the schema objects to a list so we can get items using position index
+ var schemaItems = submissions.schema.ToList();
- // For each submission we have build up a string to save to a text file
- foreach (var submission in submissions.Results)
- {
- // The submitted data for the form submission
- var submissionData = submission.Fields.ToList();
+ // We will use this to store our contents of our file to save as a text file
+ var fileContents = string.Empty;
- // For loop to match the schema position to the submission data
- for (int i = 0; i < schemaItems.Count; i++)
+ // For each submission we have build up a string to save to a text file
+ foreach (EntrySearchResult submission in submissions.Results)
{
- // Concat a string of the name of the field & its stored data
- fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine;
+ // The submitted data for the form submission
+ var submissionData = submission.Fields.ToList();
+
+ // For loop to match the schema position to the submission data
+ for (int i = 0; i < schemaItems.Count; i++)
+ {
+ // Concat a string of the name of the field & its stored data
+ fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine;
+ }
+
+ // Now save the contents to a text file
+ // Base it on the format of the record submission unique id
+ var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt");
+ File.WriteAllText(textFileName, fileContents);
+
+ // Reset fileContents to be empty again
+ fileContents = string.Empty;
}
- // Now save the contents to a text file
- // Base it on the format of the record submission unique id
- var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt");
- File.WriteAllText(textFileName, fileContents);
+ // Now we have a temp folder full of text files
+ // Generate a zip file containing them & save that
+ ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile);
- // Reset fileContents to be empty again
- fileContents = string.Empty;
- }
-
- // Now we have a temp folder full of text files
- // Generate a zip file containing them & save that
- ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile);
+ // Tidy up after ourselves & delete the temp folder of text files
+ if (Directory.Exists(tempFileDir))
+ {
+ Directory.Delete(tempFileDir, true);
+ }
- // Tidy up after ourselves & delete the temp folder of text files
- if (Directory.Exists(tempFileDir))
- {
- Directory.Delete(tempFileDir, true);
+ // Return the path where we saved the zip file containing the text files
+ return pathToSaveZipFile;
}
-
- // Return the path where we saved the zip file containing the text files
- return pathToSaveZipFile;
}
-
}
```
diff --git a/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v7.md b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v7.md
new file mode 100644
index 00000000000..c146f6501fc
--- /dev/null
+++ b/Add-ons/UmbracoForms/Developer/Extending/Adding-a-Fieldtype-v7.md
@@ -0,0 +1,99 @@
+---
+versionFrom: 7.0.0
+meta.Title: "Adding a field type to Umbraco Forms"
+---
+
+# Adding a field type to Umbraco Forms #
+
+*This builds on the "[adding a type to the provider model](Adding-a-Type.md)" chapter*
+
+## C#
+
+Add a new class to the Visual Studio solution, make it inherit from Umbraco.Forms.Core.FieldType and fill in the constructor:
+
+```csharp
+using Umbraco.Forms.Core.Data.Storage;
+using Umbraco.Forms.Core.Enums;
+using Umbraco.Forms.Core.Models;
+
+
+
+public class MyCustomField : Umbraco.Forms.Core.FieldType
+{
+ public MyCustomField()
+ {
+ this.Id = new Guid("08b8057f-06c9-4ca5-8a42-fd1fc2a46eff"); // Replace this!
+ this.Name = "My Custom Field";
+ this.Description = "Render a custom text field.";
+ this.Icon = "icon-autofill";
+ this.DataType = FieldDataType.String;
+ this.SortOrder = 10;
+ this.SupportsRegex = true;
+ }
+
+ // You can do custom validation in here which will occur when the form is submitted.
+ // Any strings returned will cause the submit to be invalid!
+ // Where as returning an empty ienumerable of strings will say that it's okay.
+ public override IEnumerable ValidateField(Form form, Field field, IEnumerable