diff --git a/16/umbraco-forms/developer/configuration/README.md b/16/umbraco-forms/developer/configuration/README.md index 60fdc2d45d1..f3e91b70e46 100644 --- a/16/umbraco-forms/developer/configuration/README.md +++ b/16/umbraco-forms/developer/configuration/README.md @@ -12,7 +12,7 @@ With Umbraco Forms it's possible to customize the functionality with various con 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.: +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 { @@ -53,6 +53,7 @@ For illustration purposes, the following structure represents the full set of op "PreviousPageButtonLabel": "Previous", "SubmitButtonLabel": "Submit", "MessageOnSubmit": "Thank you", + "MessageOnSubmitIsHtml": false, "StoreRecordsLocally": true, "AutocompleteAttribute": "", "DaysToRetainSubmittedRecordsFor": 0, @@ -89,7 +90,6 @@ For illustration purposes, the following structure represents the full set of op "DisableRecordIndexing": false, "EnableFormsApi": false, "EnableRecordingOfIpWithFormSubmission": false, - "UseSemanticFieldsetRendering": false, "DisableClientSideValidationDependencyCheck": false, "DisableRelationTracking": false, "TrackRenderedFormsStorageMethod": "HttpContextItems", @@ -97,7 +97,7 @@ For illustration purposes, the following structure represents the full set of op "EnableAdvancedValidationRules": false }, "Security": { - "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx", + "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx,js", "AllowedFileUploadExtensions": "", "EnableAntiForgeryToken": true, "SavePlainTextPasswords": false, @@ -304,6 +304,10 @@ These settings configure the default next, previous, and submit button labels. B 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`. +#### MessageOnSubmitIsHtml + +This setting needs to be a `true` or `false` value. When set to `true`, the message configured in `MessageOnSubmit` is rendered as HTML. Defaults to `false`. + #### 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`. @@ -424,14 +428,6 @@ To include this information in the saved data, set this value to `true`. If recording IPs and your site is behind a proxy, load balancer or CDN, we recommend using [ASP.NET's forwarded headers middleware](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-7.0) to ensure the correct value for the client IP is resolved. -### UseSemanticFieldsetRendering - -In Forms 12.1 amends were made to the default theme for Forms that improved accessibility. Specifically we provide the option to use alternative markup for rendering checkbox and radio button lists. These use the more semantically correct `fieldset` and `legend` elements, instead of the previously used `div` and `label`. - -Although this semantic markup is preferred, it could be a presentational breaking change for those styling the default theme. As such we have made this markup improvement optional. You can opt into using it by setting this configuration value to `true`. - -In Umbraco 13 this configuration option will be removed and the semantic rendering made the only option. - ### DisableClientSideValidationDependencyCheck When a form is rendered on the front-end website, a check is run to ensure that client-side validation framework is available and registered. diff --git a/16/umbraco-forms/developer/configuration/type-details.md b/16/umbraco-forms/developer/configuration/type-details.md index 71c8e7edc50..6cc1420f30a 100644 --- a/16/umbraco-forms/developer/configuration/type-details.md +++ b/16/umbraco-forms/developer/configuration/type-details.md @@ -1,5 +1,5 @@ --- -description: Provides details of the built-in provider types available with Umbraco Forms +description: "Provides details of the built-in provider types available with Umbraco Forms" --- # Forms Provider Type Details @@ -184,7 +184,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +reCAPTCHA Enterprise **ID:** `1BAB78CB-52B1-495C-BBC2-A46540642828` @@ -200,7 +200,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Rich Text **ID:** `1F8D45F8-76E6-4550-A0F5-9637B8454619` @@ -215,7 +215,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Single Choice **ID:** `903DF9B0-A78C-11DE-9FC1-DB7A56D89593` @@ -231,7 +231,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Short Answer **ID:** `3F92E01B-29E2-4a30-BF33-9DF5580ED52C` @@ -269,7 +269,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Change Record State **ID:** `4C40A092-0CB5-481d-96A7-A02D8E7CDB2F` @@ -284,7 +284,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Post as XML **ID:** `470EEB3A-CB15-4b08-9FC0-A2F091583332` @@ -296,6 +296,7 @@ The intention is to be able to make available details such as IDs, aliases and p * `Method` * `XsltFile` * `Fields` +* `DefaultElementForFields` * `Username` * `Password` @@ -303,7 +304,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Save As Umbraco Content Node **ID:** `89FB1E31-9F36-4e08-9D1B-AF1180D340DB` @@ -319,7 +320,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Save As XML File **ID:** `9CC5854D-61A2-48f6-9F4A-8F3BDFAFB521` @@ -371,13 +372,16 @@ The intention is to be able to make available details such as IDs, aliases and p * `ReplyToEmail` * `Subject` * `RazorViewFilePath` +* `HeaderHtml` +* `BodyHtml` +* `FooterHtml` * `Attachment`
- +Send Email With Extensible Stylesheet Language Transformations (XSLT) Template **ID:** `616edfeb-badf-414b-89dc-d8655eb85998` @@ -397,7 +401,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Send Form To URL **ID:** `FD02C929-4E7D-4f90-B9FA-13D074A76688` @@ -409,6 +413,7 @@ The intention is to be able to make available details such as IDs, aliases and p * `Method` * `StandardFields` * `Fields` +* `DefaultElementForFields` * `Username` * `Password` @@ -416,7 +421,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Slack **ID:** `bc52ab28-d3ff-42ee-af75-a5d49be83040` @@ -449,7 +454,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Datasource **ID:** `cc9f9b2a-a746-11de-9e17-681b56d89593` @@ -459,7 +464,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Get Values From Text File **ID:** `35C2053E-CBF7-4793-B27C-6E97B7671A2D` @@ -473,7 +478,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +SQL Database **ID:** `F1F5BD4D-E6AE-44ed-86CB-97661E4660B2` @@ -492,7 +497,7 @@ The intention is to be able to make available details such as IDs, aliases and p
- +Umbraco Datatype Prevalues **ID:** `EA773CAF-FEF2-491B-B5B7-6A3552B1A0E2` diff --git a/16/umbraco-forms/developer/custom-markup.md b/16/umbraco-forms/developer/custom-markup.md index 24e7a28980d..d811d538a66 100644 --- a/16/umbraco-forms/developer/custom-markup.md +++ b/16/umbraco-forms/developer/custom-markup.md @@ -43,20 +43,20 @@ The rest of the views start with FieldType, like `FieldType.Textfield.cshtml` an Contents of the `FieldType.Textfield.cshtml` view (from the default theme): ```csharp -@model Umbraco.Forms.Mvc.Models.FieldViewModel -@using Umbraco.Forms.Mvc - -placeholder="@Model.PlaceholderText"}} - @{if(Model.Mandatory || Model.Validate){data-val="true"}} - @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage"}} - @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)"}} -/> +@using Umbraco.Forms.Web +@model Umbraco.Forms.Web.Models.FieldViewModel +@{ + var maxLength = Model.GetSettingValue("MaximumLength", 255); + var fieldType = Model.GetSettingValue("FieldType", "text"); + var autocompleteAttribute = Model.GetSettingValue("AutocompleteAttribute", string.Empty); +} + placeholder="@Model.PlaceholderText" }} + @{if (string.IsNullOrEmpty(autocompleteAttribute) == false) { autocomplete="@autocompleteAttribute" }} + @{if (Model.Mandatory || Model.Validate) { data-val="true" }} + @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage" aria-required="true" }} + @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} + aria-describedby="@(Model.Id)_validation@(!string.IsNullOrEmpty(Model.ToolTip) ? $" {Model.Id}_description" : "")"/> ``` Umbraco Forms uses ASP.NET Unobtrusive Validation which is why you see attributes like `data-val` and `data-val-required`. diff --git a/16/umbraco-forms/developer/email-templates.md b/16/umbraco-forms/developer/email-templates.md index b1ad7e8d47f..9b912fa1d62 100644 --- a/16/umbraco-forms/developer/email-templates.md +++ b/16/umbraco-forms/developer/email-templates.md @@ -177,6 +177,7 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails { "FieldType.Recaptcha2.cshtml", "FieldType.Recaptcha3.cshtml", + "FieldType.RecaptchaEnterprise.cshtml", "FieldType.RichText.cshtml", "FieldType.Text.cshtml" }; diff --git a/16/umbraco-forms/developer/extending/adding-a-exporttype.md b/16/umbraco-forms/developer/extending/adding-a-exporttype.md index 9052013981a..583d5fd812a 100644 --- a/16/umbraco-forms/developer/extending/adding-a-exporttype.md +++ b/16/umbraco-forms/developer/extending/adding-a-exporttype.md @@ -6,12 +6,12 @@ Add a new class to your project and have it inherit from `Umbraco.Forms.Core.Exp ## Basic Example -You can implement the method `public override string ExportRecords(RecordExportFilter filter)` in your export provider class. You need to return a string you wish to write to a file. For example, you can generate a `.csv` (comma-separated values) file. You would perform your logic to build up a comma-separated string in the `ExportRecords` method. +You can implement the method `public override Task ExportRecordsAsync(Guid formId, RecordExportFilter filter)` in your export provider class. You need to return the string you wish to write to a file. For example, you can generate a `.csv` (comma-separated values) file. You would implement your logic to build up a comma-separated string in the `ExportRecordsAsync` method. {% hint style="info" %} -In the constructor of your provider, you will need to set the following properties: `Alias`, `FileExtension`, and `Icon`. +In the constructor of your provider, you need to set the following properties: `Alias`, `FileExtension`, and `Icon`. -The `Alias` is used to construct localization keys for the export type's label and description displayed in the backoffice. See [Localization](#localization) below for details. +The `Alias` is used to construct localization keys for the export type label and description displayed in the backoffice. See [Localization](#localization) below for details. {% endhint %} `FileExtension` is the extension such as `zip`, `txt` or `csv` of the file you will be generating and serving from the file system. @@ -22,7 +22,9 @@ In this example below we will create a single HTML file which takes all the subm ```csharp using System; -using Umbraco.Cms.Core.Hosting; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Umbraco.Extensions; using Umbraco.Forms.Core; using Umbraco.Forms.Core.Models; using Umbraco.Forms.Core.Searchers; @@ -36,10 +38,8 @@ namespace MyFormsExtensions private readonly IFormRecordSearcher _formRecordSearcher; public ExportToHtml( - IHostEnvironment hostEnvironment, IHttpContextAccessor httpContextAccessor, IFormRecordSearcher formRecordSearcher) - : base(hostEnvironment) { _httpContextAccessor = httpContextAccessor; _formRecordSearcher = formRecordSearcher; @@ -52,11 +52,11 @@ namespace MyFormsExtensions Icon = "icon-article"; } - public override string ExportRecords(RecordExportFilter filter) + public override async Task ExportRecordsAsync(Guid formId, RecordExportFilter filter) { var view = "~/Views/Partials/Forms/Export/html-report.cshtml"; EntrySearchResultCollection model = _formRecordSearcher.QueryDataBase(filter); - return ViewHelper.RenderPartialViewToString(_httpContextAccessor.GetRequiredHttpContext(), view, model); + return await ViewHelper.RenderPartialViewToString(_httpContextAccessor.GetRequiredHttpContext(), view, model); } } } @@ -92,8 +92,8 @@ namespace MyFormsExtensions ```csharp using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Forms.Core.Providers.Extensions; -using Umbraco.Forms.TestSite.Business.ExportTypes; namespace MyFormsExtensions { @@ -118,7 +118,7 @@ using System; using System.IO; using System.IO.Compression; using System.Linq; -using Umbraco.Cms.Core.Hosting; +using System.Threading.Tasks; using Umbraco.Forms.Core; using Umbraco.Forms.Core.Models; using Umbraco.Forms.Core.Searchers; @@ -129,19 +129,16 @@ namespace MyFormsExtensions { private readonly IFormRecordSearcher _formRecordSearcher; - public ExportToTextFiles( - IHostingEnvironment hostingEnvironment, - IFormRecordSearcher formRecordSearcher) - : base(hostingEnvironment) + public ExportToTextFiles(IFormRecordSearcher formRecordSearcher) { _formRecordSearcher = formRecordSearcher; - this.Name = "Export as text files"; - this.Description = "Export entries as text files inside a zip file"; - this.Alias = "exportAsTextFiles"; - this.Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB"); - this.FileExtension = "zip"; - this.Icon = "icon-zip"; + Name = "Export as text files"; + Description = "Export entries as text files inside a zip file"; + Alias = "exportAsTextFiles"; + Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB"); + FileExtension = "zip"; + Icon = "icon-zip"; } /// @@ -149,11 +146,12 @@ namespace MyFormsExtensions /// 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(); + public override Task ExportRecordsAsync(Guid formId, RecordExportFilter filter) => throw new NotImplementedException(); /// /// This gives us greater control of the export process /// + /// The form identifier. /// /// This filter contains the date range & other search parameters to limit the entries we are exporting /// @@ -162,23 +160,15 @@ namespace MyFormsExtensions /// 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) + public override Task ExportToFileAsync(Guid formId, 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('\\')); + // Get the directory + var dir = Path.GetDirectoryName(filepath); 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) { @@ -202,7 +192,7 @@ namespace MyFormsExtensions 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(); + 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; @@ -240,7 +230,7 @@ namespace MyFormsExtensions } // Return the path where we saved the zip file containing the text files - return pathToSaveZipFile; + return Task.FromResult(pathToSaveZipFile); } } } diff --git a/16/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md b/16/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md index e7535c9526d..1606fdd4588 100644 --- a/16/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md +++ b/16/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md @@ -11,6 +11,7 @@ Dynamic settings can be applied and validated as shown in the [Validate type set ```csharp using System; using System.Collections.Generic; +using System.Threading.Tasks; using Umbraco.Forms.Core; using Umbraco.Forms.Core.Models; @@ -25,8 +26,8 @@ namespace MyFormsExtensions Description = "Example prevalue source providing a fixed list of values."; } - public override List GetPreValues(Field field, Form form) => - new List + public override Task> GetPreValuesAsync(Field? field, Form? form) => + Task.FromResult(new List { new PreValue { @@ -40,7 +41,7 @@ namespace MyFormsExtensions Value = "item-two", Caption = "Item Two" } - }; + }); /// public override List ValidateSettings() @@ -85,6 +86,7 @@ This example will take a user-provided Content Node and create a custom Prevalue ```csharp using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models.PublishedContent; @@ -100,7 +102,7 @@ namespace MyFormsExtensions [Umbraco.Forms.Core.Attributes.Setting(name: "Source Node", Alias = "SourceNodeId", Description = "Node holding the Options desired.", - View = "pickers.content")] + View = "Umb.PropertyEditorUi.ContentPicker.Source")] public string SourceNodeId { get; set; } public FormPrevaluesSourceNode( ILogger logger @@ -121,7 +123,7 @@ namespace MyFormsExtensions /// /// /// List of 'Umbraco.Forms.Core.Models.PreValue' - public override List GetPreValues(Field field, Form form) + public override Task> GetPreValuesAsync(Field? field, Form? form) { List result = new List(); try @@ -159,9 +161,9 @@ namespace MyFormsExtensions } catch (Exception ex) { - _logger.LogError($"Unable to get options from FormPrevaluesSourceNode #{SourceNodeId}", ex); + _logger.LogError(ex, "Unable to get options from FormPrevaluesSourceNode #{SourceNodeId}", SourceNodeId); } - return result; + return Task.FromResult(result); } /// /// This is where any checks for Configuration validity are done. diff --git a/16/umbraco-forms/developer/extending/adding-a-type.md b/16/umbraco-forms/developer/extending/adding-a-type.md index c1b1784e3dc..f8368c999f6 100644 --- a/16/umbraco-forms/developer/extending/adding-a-type.md +++ b/16/umbraco-forms/developer/extending/adding-a-type.md @@ -20,7 +20,7 @@ public class LogWorkflow : Umbraco.Forms.Core.WorkflowType _logger = logger; } - public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) + public override Task ExecuteAsync(WorkflowExecutionContext context) { throw new NotImplementedException(); } @@ -31,7 +31,7 @@ public class LogWorkflow : Umbraco.Forms.Core.WorkflowType } ``` -When you implement this class you get two methods added. One of them is Execute which performs the execution of the workflow and the other is a method which validates the workflow settings, we will get back to these settings later on. +When you implement this class you get two methods added. `ExecuteAsync` performs the execution of the workflow. `ValidateSettings` validates the workflow settings, which are covered later in this article. Any dependencies required that are registered with the dependency injection container can be provided via the constructor. @@ -61,7 +61,7 @@ Now that we have a basic class setup, we would like to pass setting items to the ```csharp [Umbraco.Forms.Core.Attributes.Setting("Log Header", Description = "Log item header", - View = "TextField")] + View = "Umb.PropertyEditorUi.TextBox")] public string LogHeader { get; set; } ``` @@ -72,12 +72,12 @@ With the attribute in place, the property value is set every time the class is i ```csharp [Umbraco.Forms.Core.Attributes.Setting("Document ID", Description = "Node the log entry belongs to", - View = "Pickers.Content")] + View = "Umb.PropertyEditorUi.ContentPicker.Source")] public string Document { get; set; } -public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) { +public override Task ExecuteAsync(WorkflowExecutionContext context) { _logger.LogInformation("Record submitted from: {IP}", context.Record.IP); - return WorkflowExecutionStatus.Completed; + return Task.FromResult(WorkflowExecutionStatus.Completed); } ``` @@ -168,7 +168,7 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare // Added a new setting when we add our field to the form [Umbraco.Forms.Core.Attributes.Setting("Max length", Description = "Max length", - View = "TextField")] + View = "Umb.PropertyEditorUi.TextBox")] public string MaxNumberOfChars { get; set; } public TextareaWithCount() @@ -180,9 +180,9 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare this.Name = "Long Answer with Limit"; } - public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, List errors) + public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, IFieldTypeStorage fieldTypeStorage, List errors) { - var baseValidation = base.ValidateField(form, field, postedValues, context, placeholderParsingService, errors); + var baseValidation = base.ValidateField(form, field, postedValues, context, placeholderParsingService, fieldTypeStorage, errors); var value = postedValues.FirstOrDefault(); if (value != null && value.ToString().Length < int.Parse(MaxNumberOfChars)) @@ -199,7 +199,7 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare } ``` -As discussed in the previous section, you must also register the extended field type within a composer. You also need to create the the backoffice field type view. +As discussed in the previous section, you must also register the extended field type within a composer. **Composer:** @@ -213,10 +213,4 @@ public class UmbracoFormsCustomProvidersComposer : IComposer } ``` -**Backoffice View:** - -Add a new HTML file as per the name of the field class (e.g. `textareawithcount.html`) to `\wwwroot\App_Plugins\umbracoforms\Backoffice\Common\FieldTypes\` within your project. For this example, we can copy the original `textarea.html` file used by the standard 'Long Answer' field. - -The AngularJS client-side files are shipped with Umbraco Forms as part of a Razor Class Library. So you won't find these files on disk when you install the package. - -However if you do want to reference them you can view and extract them from the [`Umbraco.Forms.StaticAssets` NuGet package](https://nuget.info/packages/Umbraco.Forms.StaticAssets). +If your custom field type requires a different presentation in the backoffice, you can register client-side components for it. For more information, see the [Adding A Field Type To Umbraco Forms](adding-a-fieldtype.md) article. diff --git a/16/umbraco-forms/developer/extending/adding-a-validation-pattern.md b/16/umbraco-forms/developer/extending/adding-a-validation-pattern.md index b304d9ffc13..6198e354215 100644 --- a/16/umbraco-forms/developer/extending/adding-a-validation-pattern.md +++ b/16/umbraco-forms/developer/extending/adding-a-validation-pattern.md @@ -26,7 +26,7 @@ The following example shows the implementation of a pattern for a United Kingdom ```csharp using Umbraco.Forms.Core.Interfaces; -namespace Umbraco.Forms.TestSite.Business.ValidationPatterns +namespace MyFormsExtensions.ValidationPatterns { public class UkPostCode : IValidationPattern { diff --git a/16/umbraco-forms/developer/extending/adding-a-workflowtype.md b/16/umbraco-forms/developer/extending/adding-a-workflowtype.md index 019f24f030d..bb3975d64ad 100644 --- a/16/umbraco-forms/developer/extending/adding-a-workflowtype.md +++ b/16/umbraco-forms/developer/extending/adding-a-workflowtype.md @@ -5,15 +5,13 @@ Add a new class to your project and have it inherit from `Umbraco.Forms.Core.WorkflowType`, and implement the class. For this sample, we will focus on the execute method. This method processes the current record (the data submitted by the form) and have the ability to change data and state. ```csharp -using Serilog; using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Umbraco.Forms.Core; -using Umbraco.Forms.Core.Data.Storage; using Umbraco.Forms.Core.Enums; using Umbraco.Forms.Core.Persistence.Dtos; -using Microsoft.Extensions.Logging; -using Umbraco.Core.Composing; namespace MyFormsExtensions { diff --git a/16/umbraco-forms/developer/extending/customize-default-workflows.md b/16/umbraco-forms/developer/extending/customize-default-workflows.md index ed47104e6bf..d5830e6ffe8 100644 --- a/16/umbraco-forms/developer/extending/customize-default-workflows.md +++ b/16/umbraco-forms/developer/extending/customize-default-workflows.md @@ -27,11 +27,11 @@ Firstly, the custom workflow: ```csharp using System; using System.Collections.Generic; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Umbraco.Forms.Core; using Umbraco.Forms.Core.Attributes; using Umbraco.Forms.Core.Enums; -using Umbraco.Forms.Core.Persistence.Dtos; namespace MyNamespace { @@ -64,10 +64,10 @@ namespace MyNamespace return exs; } - public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) + public override Task ExecuteAsync(WorkflowExecutionContext context) { - _logger.LogInformation($"'{Message}' written at {DateTime.Now}"); - return WorkflowExecutionStatus.Completed; + _logger.LogInformation("'{Message}' written at {Date}", Message, DateTime.Now); + return Task.FromResult(WorkflowExecutionStatus.Completed); } } } @@ -150,7 +150,6 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Extensions; using Umbraco.Forms.Core.Providers; -using Umbraco.Forms.Testsite.Business.Workflows; using Umbraco.Forms.Web.Behaviors; namespace MyNamespace diff --git a/16/umbraco-forms/developer/themes.md b/16/umbraco-forms/developer/themes.md index 4d590390b4f..a60efea3cf4 100644 --- a/16/umbraco-forms/developer/themes.md +++ b/16/umbraco-forms/developer/themes.md @@ -136,7 +136,14 @@ Files which can be overridden: * Render.cshtml (overrides the entire form - usually not needed) * Form.cshtml (overrides the generation of the fields on the current page) * Script.cshtml (overrides the way files are included with the form) +* ScrollToFormScript.cshtml (overrides the script used to scroll to the form after submission) +* Submitted.cshtml (overrides the message shown when the form is submitted) +* DatePicker.cshtml (overrides the date picker initialization script) +* MultiPageFormPagingDetails.cshtml (overrides the paging details shown on multi-page forms) +* MultiPageFormSummary.cshtml (overrides the summary shown on multi-page forms) * /Fieldtypes/FieldType.\*.cshtml (overrides a specific view for a field) +* /Fieldtypes/FieldType.\*.ReadOnly.cshtml (overrides the read-only view for a field, used on multi-page form summaries) +* /Fieldtypes/ReadOnly.cshtml (overrides the generic read-only view for fields) ## Helper Methods @@ -145,7 +152,7 @@ Files which can be overridden: Sets the primary form theme stylesheet path. This overrides an already assigned stylesheet and will be rendered out when inserting the form into the page ```csharp -Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") +Html.SetFormThemeCssFile("~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") ``` ### AddFormThemeScriptFile diff --git a/16/umbraco-forms/developer/working-with-data.md b/16/umbraco-forms/developer/working-with-data.md index b5d42a577bf..7d0a24ee17e 100644 --- a/16/umbraco-forms/developer/working-with-data.md +++ b/16/umbraco-forms/developer/working-with-data.md @@ -13,7 +13,7 @@ The methods can be found by injecting the `Umbraco.Forms.Core.Services.IRecordRe ### GetApprovedRecordsFromPage ```csharp -PagedResult GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` Returns all records with the state set to approved from all Forms on the Umbraco page with the id = `pageId` . @@ -21,46 +21,46 @@ Returns all records with the state set to approved from all Forms on the Umbraco ### GetApprovedRecordsFromFormOnPage ```csharp -PagedResult GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records with the state set to approved from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetApprovedRecordsFromForm ```csharp -PagedResult GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the Form with the ID = `formId` as a `PagedResult`. +Returns all records with the state set to approved from the Form with the ID = `formId` as a `PagedModel`. ### GetRecordsFromPage ```csharp -PagedResult GetRecordsFromPage(int pageId, int pageNumber, int pageSize) +PagedModel GetRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` -Returns all records from all Forms on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records from all Forms on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetRecordsFromFormOnPage ```csharp -PagedResult GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) +PagedModel GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) ``` -Returns all records from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetRecordsFromForm ```csharp -PagedResult GetRecordsFromForm(Guid formId, int pageNumber, int pageSize) +PagedModel GetRecordsFromForm(Guid formId, int pageNumber, int pageSize) ``` -Returns all records from the Form with the ID = formId as a `PagedResult`. +Returns all records from the Form with the ID = formId as a `PagedModel`. ## The returned objects -All of these methods will return an object of type `PagedResult` so you can iterate through the `Record` objects. +All of these methods will return an object of type `PagedModel` so you can iterate through the `Record` objects. The properties available on a `Record` are: @@ -86,9 +86,8 @@ This extension method handle multi value fields by comma separating the values. Sample script that is outputting comments using a Form created with the default comment Form template. ```csharp -@using Umbraco.Core; -@using Umbraco.Cms.Core.Composing; @using Umbraco.Forms.Core.Extensions; +@using Umbraco.Forms.Core.Services; @inject IRecordReaderService _recordReaderService;
    @@ -133,8 +132,8 @@ Here is a sample code for retrieving a record in a view. Record? record; string submittedEmail; - if (Guid.TryParse(TempData["UmbracoFormSubmitted"]?.ToString(), out Guid formId) && - Guid.TryParse(TempData["Forms_Current_Record_id"]?.ToString(), out Guid recordId)) + if (Guid.TryParse(TempData["UmbracoFormSubmitted"]?.ToString(), out formId) && + Guid.TryParse(TempData["Forms_Current_Record_id"]?.ToString(), out recordId)) { form = _formService.Get(formId); diff --git a/16/umbraco-forms/editor/attaching-workflows/workflow-types.md b/16/umbraco-forms/editor/attaching-workflows/workflow-types.md index 2671a4e64d7..30bee2fb946 100644 --- a/16/umbraco-forms/editor/attaching-workflows/workflow-types.md +++ b/16/umbraco-forms/editor/attaching-workflows/workflow-types.md @@ -28,9 +28,10 @@ Used to post the Form as an XML to a specified URL. The following configuration * Workflow Name * URL (required) -* Method -* XsltFile - used to transform the XML -* Headers - map the needed files +* Method - POST, GET, PUT or DELETE +* XSLT File - used to transform the XML +* Fields - map form fields to values that are sent as HTTP headers with the request +* Default Element For Fields - choose whether the field caption or alias is used as the XML element name * User * Password @@ -158,6 +159,7 @@ Sends the Form to a URL either as a HTTP POST or GET. The following configuratio * Method (required) - POST, GET, PUT or DELETE * Standard Fields - optionally include and map standard form information such as name and page URL * Fields - map the needed fields +* Default Element For Fields - when no fields are mapped, choose whether the field caption or alias is used as the element name * User * Password diff --git a/17/umbraco-forms/developer/configuration/README.md b/17/umbraco-forms/developer/configuration/README.md index deb7cdda01a..6e5eb446304 100644 --- a/17/umbraco-forms/developer/configuration/README.md +++ b/17/umbraco-forms/developer/configuration/README.md @@ -53,6 +53,7 @@ For illustration purposes, the following structure represents the full set of op "PreviousPageButtonLabel": "Previous", "SubmitButtonLabel": "Submit", "MessageOnSubmit": "Thank you", + "MessageOnSubmitIsHtml": false, "StoreRecordsLocally": true, "AutocompleteAttribute": "", "DaysToRetainSubmittedRecordsFor": 0, @@ -89,7 +90,6 @@ For illustration purposes, the following structure represents the full set of op "DisableRecordIndexing": false, "EnableFormsApi": false, "EnableRecordingOfIpWithFormSubmission": false, - "UseSemanticFieldsetRendering": false, "DisableClientSideValidationDependencyCheck": false, "DisableRelationTracking": false, "TrackRenderedFormsStorageMethod": "HttpContextItems", @@ -103,7 +103,7 @@ For illustration purposes, the following structure represents the full set of op } }, "Security": { - "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx", + "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx,js", "AllowedFileUploadExtensions": "", "EnableAntiForgeryToken": true, "SavePlainTextPasswords": false, @@ -310,6 +310,10 @@ These settings configure the default next, previous, and submit button labels. B 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`. +#### MessageOnSubmitIsHtml + +This setting needs to be a `true` or `false` value. When set to `true`, the message configured in `MessageOnSubmit` is rendered as HTML. Defaults to `false`. + #### 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`. @@ -432,14 +436,6 @@ To include this information in the saved data, set this value to `true`. If recording IPs and your site is behind a proxy, load balancer or CDN, we recommend using [ASP.NET's forwarded headers middleware](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-7.0) to ensure the correct value for the client IP is resolved. -### UseSemanticFieldsetRendering - -In Forms 12.1 amends were made to the default theme for Forms that improved accessibility. Specifically we provide the option to use alternative markup for rendering checkbox and radio button lists. These use the more semantically correct `fieldset` and `legend` elements, instead of the previously used `div` and `label`. - -Although this semantic markup is preferred, it could be a presentational breaking change for those styling the default theme. As such we have made this markup improvement optional. You can opt into using it by setting this configuration value to `true`. - -In Umbraco 13 this configuration option will be removed and the semantic rendering made the only option. - ### DisableClientSideValidationDependencyCheck When a form is rendered on the front-end website, a check is run to ensure that client-side validation framework is available and registered. diff --git a/17/umbraco-forms/developer/configuration/type-details.md b/17/umbraco-forms/developer/configuration/type-details.md index b488f5b5817..6cc1420f30a 100644 --- a/17/umbraco-forms/developer/configuration/type-details.md +++ b/17/umbraco-forms/developer/configuration/type-details.md @@ -296,6 +296,7 @@ The intention is to be able to make available details such as IDs, aliases and p * `Method` * `XsltFile` * `Fields` +* `DefaultElementForFields` * `Username` * `Password` @@ -371,6 +372,9 @@ The intention is to be able to make available details such as IDs, aliases and p * `ReplyToEmail` * `Subject` * `RazorViewFilePath` +* `HeaderHtml` +* `BodyHtml` +* `FooterHtml` * `Attachment` @@ -409,6 +413,7 @@ The intention is to be able to make available details such as IDs, aliases and p * `Method` * `StandardFields` * `Fields` +* `DefaultElementForFields` * `Username` * `Password` diff --git a/17/umbraco-forms/developer/custom-markup.md b/17/umbraco-forms/developer/custom-markup.md index 24e7a28980d..d811d538a66 100644 --- a/17/umbraco-forms/developer/custom-markup.md +++ b/17/umbraco-forms/developer/custom-markup.md @@ -43,20 +43,20 @@ The rest of the views start with FieldType, like `FieldType.Textfield.cshtml` an Contents of the `FieldType.Textfield.cshtml` view (from the default theme): ```csharp -@model Umbraco.Forms.Mvc.Models.FieldViewModel -@using Umbraco.Forms.Mvc - -placeholder="@Model.PlaceholderText"}} - @{if(Model.Mandatory || Model.Validate){data-val="true"}} - @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage"}} - @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)"}} -/> +@using Umbraco.Forms.Web +@model Umbraco.Forms.Web.Models.FieldViewModel +@{ + var maxLength = Model.GetSettingValue("MaximumLength", 255); + var fieldType = Model.GetSettingValue("FieldType", "text"); + var autocompleteAttribute = Model.GetSettingValue("AutocompleteAttribute", string.Empty); +} + placeholder="@Model.PlaceholderText" }} + @{if (string.IsNullOrEmpty(autocompleteAttribute) == false) { autocomplete="@autocompleteAttribute" }} + @{if (Model.Mandatory || Model.Validate) { data-val="true" }} + @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage" aria-required="true" }} + @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} + aria-describedby="@(Model.Id)_validation@(!string.IsNullOrEmpty(Model.ToolTip) ? $" {Model.Id}_description" : "")"/> ``` Umbraco Forms uses ASP.NET Unobtrusive Validation which is why you see attributes like `data-val` and `data-val-required`. diff --git a/17/umbraco-forms/developer/email-templates.md b/17/umbraco-forms/developer/email-templates.md index b1ad7e8d47f..9b912fa1d62 100644 --- a/17/umbraco-forms/developer/email-templates.md +++ b/17/umbraco-forms/developer/email-templates.md @@ -177,6 +177,7 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails { "FieldType.Recaptcha2.cshtml", "FieldType.Recaptcha3.cshtml", + "FieldType.RecaptchaEnterprise.cshtml", "FieldType.RichText.cshtml", "FieldType.Text.cshtml" }; diff --git a/17/umbraco-forms/developer/extending/adding-a-exporttype.md b/17/umbraco-forms/developer/extending/adding-a-exporttype.md index 35e772f181c..583d5fd812a 100644 --- a/17/umbraco-forms/developer/extending/adding-a-exporttype.md +++ b/17/umbraco-forms/developer/extending/adding-a-exporttype.md @@ -23,6 +23,8 @@ In this example below we will create a single HTML file which takes all the subm ```csharp using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Umbraco.Extensions; using Umbraco.Forms.Core; using Umbraco.Forms.Core.Models; using Umbraco.Forms.Core.Searchers; @@ -50,12 +52,11 @@ namespace MyFormsExtensions Icon = "icon-article"; } - public override Task ExportRecordsAsync(Guid formId, RecordExportFilter filter) + public override async Task ExportRecordsAsync(Guid formId, RecordExportFilter filter) { var view = "~/Views/Partials/Forms/Export/html-report.cshtml"; EntrySearchResultCollection model = _formRecordSearcher.QueryDataBase(filter); - return Task.FromResult( - ViewHelper.RenderPartialViewToString(_httpContextAccessor.GetRequiredHttpContext(), view, model)); + return await ViewHelper.RenderPartialViewToString(_httpContextAccessor.GetRequiredHttpContext(), view, model); } } } @@ -91,8 +92,8 @@ namespace MyFormsExtensions ```csharp using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Forms.Core.Providers.Extensions; -using Umbraco.Forms.TestSite.Business.ExportTypes; namespace MyFormsExtensions { @@ -191,7 +192,7 @@ namespace MyFormsExtensions 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(); + 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; diff --git a/17/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md b/17/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md index e7535c9526d..1606fdd4588 100644 --- a/17/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md +++ b/17/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md @@ -11,6 +11,7 @@ Dynamic settings can be applied and validated as shown in the [Validate type set ```csharp using System; using System.Collections.Generic; +using System.Threading.Tasks; using Umbraco.Forms.Core; using Umbraco.Forms.Core.Models; @@ -25,8 +26,8 @@ namespace MyFormsExtensions Description = "Example prevalue source providing a fixed list of values."; } - public override List GetPreValues(Field field, Form form) => - new List + public override Task> GetPreValuesAsync(Field? field, Form? form) => + Task.FromResult(new List { new PreValue { @@ -40,7 +41,7 @@ namespace MyFormsExtensions Value = "item-two", Caption = "Item Two" } - }; + }); /// public override List ValidateSettings() @@ -85,6 +86,7 @@ This example will take a user-provided Content Node and create a custom Prevalue ```csharp using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models.PublishedContent; @@ -100,7 +102,7 @@ namespace MyFormsExtensions [Umbraco.Forms.Core.Attributes.Setting(name: "Source Node", Alias = "SourceNodeId", Description = "Node holding the Options desired.", - View = "pickers.content")] + View = "Umb.PropertyEditorUi.ContentPicker.Source")] public string SourceNodeId { get; set; } public FormPrevaluesSourceNode( ILogger logger @@ -121,7 +123,7 @@ namespace MyFormsExtensions /// /// /// List of 'Umbraco.Forms.Core.Models.PreValue' - public override List GetPreValues(Field field, Form form) + public override Task> GetPreValuesAsync(Field? field, Form? form) { List result = new List(); try @@ -159,9 +161,9 @@ namespace MyFormsExtensions } catch (Exception ex) { - _logger.LogError($"Unable to get options from FormPrevaluesSourceNode #{SourceNodeId}", ex); + _logger.LogError(ex, "Unable to get options from FormPrevaluesSourceNode #{SourceNodeId}", SourceNodeId); } - return result; + return Task.FromResult(result); } /// /// This is where any checks for Configuration validity are done. diff --git a/17/umbraco-forms/developer/extending/adding-a-type.md b/17/umbraco-forms/developer/extending/adding-a-type.md index c1b1784e3dc..f8368c999f6 100644 --- a/17/umbraco-forms/developer/extending/adding-a-type.md +++ b/17/umbraco-forms/developer/extending/adding-a-type.md @@ -20,7 +20,7 @@ public class LogWorkflow : Umbraco.Forms.Core.WorkflowType _logger = logger; } - public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) + public override Task ExecuteAsync(WorkflowExecutionContext context) { throw new NotImplementedException(); } @@ -31,7 +31,7 @@ public class LogWorkflow : Umbraco.Forms.Core.WorkflowType } ``` -When you implement this class you get two methods added. One of them is Execute which performs the execution of the workflow and the other is a method which validates the workflow settings, we will get back to these settings later on. +When you implement this class you get two methods added. `ExecuteAsync` performs the execution of the workflow. `ValidateSettings` validates the workflow settings, which are covered later in this article. Any dependencies required that are registered with the dependency injection container can be provided via the constructor. @@ -61,7 +61,7 @@ Now that we have a basic class setup, we would like to pass setting items to the ```csharp [Umbraco.Forms.Core.Attributes.Setting("Log Header", Description = "Log item header", - View = "TextField")] + View = "Umb.PropertyEditorUi.TextBox")] public string LogHeader { get; set; } ``` @@ -72,12 +72,12 @@ With the attribute in place, the property value is set every time the class is i ```csharp [Umbraco.Forms.Core.Attributes.Setting("Document ID", Description = "Node the log entry belongs to", - View = "Pickers.Content")] + View = "Umb.PropertyEditorUi.ContentPicker.Source")] public string Document { get; set; } -public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) { +public override Task ExecuteAsync(WorkflowExecutionContext context) { _logger.LogInformation("Record submitted from: {IP}", context.Record.IP); - return WorkflowExecutionStatus.Completed; + return Task.FromResult(WorkflowExecutionStatus.Completed); } ``` @@ -168,7 +168,7 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare // Added a new setting when we add our field to the form [Umbraco.Forms.Core.Attributes.Setting("Max length", Description = "Max length", - View = "TextField")] + View = "Umb.PropertyEditorUi.TextBox")] public string MaxNumberOfChars { get; set; } public TextareaWithCount() @@ -180,9 +180,9 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare this.Name = "Long Answer with Limit"; } - public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, List errors) + public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, IFieldTypeStorage fieldTypeStorage, List errors) { - var baseValidation = base.ValidateField(form, field, postedValues, context, placeholderParsingService, errors); + var baseValidation = base.ValidateField(form, field, postedValues, context, placeholderParsingService, fieldTypeStorage, errors); var value = postedValues.FirstOrDefault(); if (value != null && value.ToString().Length < int.Parse(MaxNumberOfChars)) @@ -199,7 +199,7 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare } ``` -As discussed in the previous section, you must also register the extended field type within a composer. You also need to create the the backoffice field type view. +As discussed in the previous section, you must also register the extended field type within a composer. **Composer:** @@ -213,10 +213,4 @@ public class UmbracoFormsCustomProvidersComposer : IComposer } ``` -**Backoffice View:** - -Add a new HTML file as per the name of the field class (e.g. `textareawithcount.html`) to `\wwwroot\App_Plugins\umbracoforms\Backoffice\Common\FieldTypes\` within your project. For this example, we can copy the original `textarea.html` file used by the standard 'Long Answer' field. - -The AngularJS client-side files are shipped with Umbraco Forms as part of a Razor Class Library. So you won't find these files on disk when you install the package. - -However if you do want to reference them you can view and extract them from the [`Umbraco.Forms.StaticAssets` NuGet package](https://nuget.info/packages/Umbraco.Forms.StaticAssets). +If your custom field type requires a different presentation in the backoffice, you can register client-side components for it. For more information, see the [Adding A Field Type To Umbraco Forms](adding-a-fieldtype.md) article. diff --git a/17/umbraco-forms/developer/extending/adding-a-validation-pattern.md b/17/umbraco-forms/developer/extending/adding-a-validation-pattern.md index b304d9ffc13..6198e354215 100644 --- a/17/umbraco-forms/developer/extending/adding-a-validation-pattern.md +++ b/17/umbraco-forms/developer/extending/adding-a-validation-pattern.md @@ -26,7 +26,7 @@ The following example shows the implementation of a pattern for a United Kingdom ```csharp using Umbraco.Forms.Core.Interfaces; -namespace Umbraco.Forms.TestSite.Business.ValidationPatterns +namespace MyFormsExtensions.ValidationPatterns { public class UkPostCode : IValidationPattern { diff --git a/17/umbraco-forms/developer/extending/adding-a-workflowtype.md b/17/umbraco-forms/developer/extending/adding-a-workflowtype.md index 019f24f030d..bb3975d64ad 100644 --- a/17/umbraco-forms/developer/extending/adding-a-workflowtype.md +++ b/17/umbraco-forms/developer/extending/adding-a-workflowtype.md @@ -5,15 +5,13 @@ Add a new class to your project and have it inherit from `Umbraco.Forms.Core.WorkflowType`, and implement the class. For this sample, we will focus on the execute method. This method processes the current record (the data submitted by the form) and have the ability to change data and state. ```csharp -using Serilog; using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Umbraco.Forms.Core; -using Umbraco.Forms.Core.Data.Storage; using Umbraco.Forms.Core.Enums; using Umbraco.Forms.Core.Persistence.Dtos; -using Microsoft.Extensions.Logging; -using Umbraco.Core.Composing; namespace MyFormsExtensions { diff --git a/17/umbraco-forms/developer/extending/customize-default-workflows.md b/17/umbraco-forms/developer/extending/customize-default-workflows.md index ed47104e6bf..d5830e6ffe8 100644 --- a/17/umbraco-forms/developer/extending/customize-default-workflows.md +++ b/17/umbraco-forms/developer/extending/customize-default-workflows.md @@ -27,11 +27,11 @@ Firstly, the custom workflow: ```csharp using System; using System.Collections.Generic; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Umbraco.Forms.Core; using Umbraco.Forms.Core.Attributes; using Umbraco.Forms.Core.Enums; -using Umbraco.Forms.Core.Persistence.Dtos; namespace MyNamespace { @@ -64,10 +64,10 @@ namespace MyNamespace return exs; } - public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) + public override Task ExecuteAsync(WorkflowExecutionContext context) { - _logger.LogInformation($"'{Message}' written at {DateTime.Now}"); - return WorkflowExecutionStatus.Completed; + _logger.LogInformation("'{Message}' written at {Date}", Message, DateTime.Now); + return Task.FromResult(WorkflowExecutionStatus.Completed); } } } @@ -150,7 +150,6 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Extensions; using Umbraco.Forms.Core.Providers; -using Umbraco.Forms.Testsite.Business.Workflows; using Umbraco.Forms.Web.Behaviors; namespace MyNamespace diff --git a/17/umbraco-forms/developer/healthchecks/README.md b/17/umbraco-forms/developer/healthchecks/README.md index b7535dec496..0602a9b07ea 100644 --- a/17/umbraco-forms/developer/healthchecks/README.md +++ b/17/umbraco-forms/developer/healthchecks/README.md @@ -113,3 +113,11 @@ To support this, we provide the following SQL scripts: * Revert database integrity schema changes for 8.7.0+ - [8.7.0-apply-keys-and-indexes\_revert](apply-keys.md#revert-application-of-keys-and-indexes) * Revert database integrity schema changes for 8.7.0+ (Forms in database tables) - [8.7.0-apply-keys-and-indexes-forms-in-db\_revert](forms-in-the-database-apply-keys.md#reverting-the-application-of-keys-and-indexes) + +## Analytics Processing Health Check + +Running this health check will verify that the daily summary processing for [Forms analytics](../../editor/analytics.md) is up to date. + +Analytics data is summarized once per day by a background task. The task runs on application startup and on a recurring schedule. If one or more dates have not been processed, analytics data may be incomplete. Queries may also fall back to slower live calculations. + +The health check reports a warning when unprocessed dates are found. The background processing task should resolve this automatically. If the warning persists, check the Umbraco log for errors during analytics processing. diff --git a/17/umbraco-forms/developer/themes.md b/17/umbraco-forms/developer/themes.md index 52f139a4b4d..94caad4da62 100644 --- a/17/umbraco-forms/developer/themes.md +++ b/17/umbraco-forms/developer/themes.md @@ -135,7 +135,14 @@ Files which can be overridden: * Render.cshtml (overrides the entire form - usually not needed) * Form.cshtml (overrides the generation of the fields on the current page) * Script.cshtml (overrides the way files are included with the form) +* ScrollToFormScript.cshtml (overrides the script used to scroll to the form after submission) +* Submitted.cshtml (overrides the message shown when the form is submitted) +* DatePicker.cshtml (overrides the date picker initialization script) +* MultiPageFormPagingDetails.cshtml (overrides the paging details shown on multi-page forms) +* MultiPageFormSummary.cshtml (overrides the summary shown on multi-page forms) * /Fieldtypes/FieldType.\*.cshtml (overrides a specific view for a field) +* /Fieldtypes/FieldType.\*.ReadOnly.cshtml (overrides the read-only view for a field, used on multi-page form summaries) +* /Fieldtypes/ReadOnly.cshtml (overrides the generic read-only view for fields) ## Helper Methods @@ -144,7 +151,7 @@ Files which can be overridden: Sets the primary form theme stylesheet path. This overrides an already assigned stylesheet and will be rendered out when inserting the form into the page ```csharp -Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") +Html.SetFormThemeCssFile("~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") ``` ### AddFormThemeScriptFile diff --git a/17/umbraco-forms/developer/working-with-data.md b/17/umbraco-forms/developer/working-with-data.md index b5d42a577bf..7d0a24ee17e 100644 --- a/17/umbraco-forms/developer/working-with-data.md +++ b/17/umbraco-forms/developer/working-with-data.md @@ -13,7 +13,7 @@ The methods can be found by injecting the `Umbraco.Forms.Core.Services.IRecordRe ### GetApprovedRecordsFromPage ```csharp -PagedResult GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` Returns all records with the state set to approved from all Forms on the Umbraco page with the id = `pageId` . @@ -21,46 +21,46 @@ Returns all records with the state set to approved from all Forms on the Umbraco ### GetApprovedRecordsFromFormOnPage ```csharp -PagedResult GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records with the state set to approved from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetApprovedRecordsFromForm ```csharp -PagedResult GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the Form with the ID = `formId` as a `PagedResult`. +Returns all records with the state set to approved from the Form with the ID = `formId` as a `PagedModel`. ### GetRecordsFromPage ```csharp -PagedResult GetRecordsFromPage(int pageId, int pageNumber, int pageSize) +PagedModel GetRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` -Returns all records from all Forms on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records from all Forms on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetRecordsFromFormOnPage ```csharp -PagedResult GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) +PagedModel GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) ``` -Returns all records from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetRecordsFromForm ```csharp -PagedResult GetRecordsFromForm(Guid formId, int pageNumber, int pageSize) +PagedModel GetRecordsFromForm(Guid formId, int pageNumber, int pageSize) ``` -Returns all records from the Form with the ID = formId as a `PagedResult`. +Returns all records from the Form with the ID = formId as a `PagedModel`. ## The returned objects -All of these methods will return an object of type `PagedResult` so you can iterate through the `Record` objects. +All of these methods will return an object of type `PagedModel` so you can iterate through the `Record` objects. The properties available on a `Record` are: @@ -86,9 +86,8 @@ This extension method handle multi value fields by comma separating the values. Sample script that is outputting comments using a Form created with the default comment Form template. ```csharp -@using Umbraco.Core; -@using Umbraco.Cms.Core.Composing; @using Umbraco.Forms.Core.Extensions; +@using Umbraco.Forms.Core.Services; @inject IRecordReaderService _recordReaderService;
      @@ -133,8 +132,8 @@ Here is a sample code for retrieving a record in a view. Record? record; string submittedEmail; - if (Guid.TryParse(TempData["UmbracoFormSubmitted"]?.ToString(), out Guid formId) && - Guid.TryParse(TempData["Forms_Current_Record_id"]?.ToString(), out Guid recordId)) + if (Guid.TryParse(TempData["UmbracoFormSubmitted"]?.ToString(), out formId) && + Guid.TryParse(TempData["Forms_Current_Record_id"]?.ToString(), out recordId)) { form = _formService.Get(formId); diff --git a/17/umbraco-forms/editor/attaching-workflows/workflow-types.md b/17/umbraco-forms/editor/attaching-workflows/workflow-types.md index 2671a4e64d7..30bee2fb946 100644 --- a/17/umbraco-forms/editor/attaching-workflows/workflow-types.md +++ b/17/umbraco-forms/editor/attaching-workflows/workflow-types.md @@ -28,9 +28,10 @@ Used to post the Form as an XML to a specified URL. The following configuration * Workflow Name * URL (required) -* Method -* XsltFile - used to transform the XML -* Headers - map the needed files +* Method - POST, GET, PUT or DELETE +* XSLT File - used to transform the XML +* Fields - map form fields to values that are sent as HTTP headers with the request +* Default Element For Fields - choose whether the field caption or alias is used as the XML element name * User * Password @@ -158,6 +159,7 @@ Sends the Form to a URL either as a HTTP POST or GET. The following configuratio * Method (required) - POST, GET, PUT or DELETE * Standard Fields - optionally include and map standard form information such as name and page URL * Fields - map the needed fields +* Default Element For Fields - when no fields are mapped, choose whether the field caption or alias is used as the element name * User * Password diff --git a/17/umbraco-forms/upgrading/version-specific.md b/17/umbraco-forms/upgrading/version-specific.md index f2162fb78f4..2bc0e527721 100644 --- a/17/umbraco-forms/upgrading/version-specific.md +++ b/17/umbraco-forms/upgrading/version-specific.md @@ -14,7 +14,7 @@ If you are upgrading to a minor or patch version, you can find the details about ## Version Specific Upgrade Notes History -Version 17 of Umbraco Forms has a minimum dependency on Umbraco CMS core of `17.0.0`. It runs on .NET 9. +Version 17 of Umbraco Forms has a minimum dependency on Umbraco CMS core of `17.0.0`. It runs on .NET 10. ## Legacy version specific upgrade notes diff --git a/18/umbraco-forms/developer/configuration/README.md b/18/umbraco-forms/developer/configuration/README.md index deb7cdda01a..6e5eb446304 100644 --- a/18/umbraco-forms/developer/configuration/README.md +++ b/18/umbraco-forms/developer/configuration/README.md @@ -53,6 +53,7 @@ For illustration purposes, the following structure represents the full set of op "PreviousPageButtonLabel": "Previous", "SubmitButtonLabel": "Submit", "MessageOnSubmit": "Thank you", + "MessageOnSubmitIsHtml": false, "StoreRecordsLocally": true, "AutocompleteAttribute": "", "DaysToRetainSubmittedRecordsFor": 0, @@ -89,7 +90,6 @@ For illustration purposes, the following structure represents the full set of op "DisableRecordIndexing": false, "EnableFormsApi": false, "EnableRecordingOfIpWithFormSubmission": false, - "UseSemanticFieldsetRendering": false, "DisableClientSideValidationDependencyCheck": false, "DisableRelationTracking": false, "TrackRenderedFormsStorageMethod": "HttpContextItems", @@ -103,7 +103,7 @@ For illustration purposes, the following structure represents the full set of op } }, "Security": { - "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx", + "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx,js", "AllowedFileUploadExtensions": "", "EnableAntiForgeryToken": true, "SavePlainTextPasswords": false, @@ -310,6 +310,10 @@ These settings configure the default next, previous, and submit button labels. B 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`. +#### MessageOnSubmitIsHtml + +This setting needs to be a `true` or `false` value. When set to `true`, the message configured in `MessageOnSubmit` is rendered as HTML. Defaults to `false`. + #### 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`. @@ -432,14 +436,6 @@ To include this information in the saved data, set this value to `true`. If recording IPs and your site is behind a proxy, load balancer or CDN, we recommend using [ASP.NET's forwarded headers middleware](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-7.0) to ensure the correct value for the client IP is resolved. -### UseSemanticFieldsetRendering - -In Forms 12.1 amends were made to the default theme for Forms that improved accessibility. Specifically we provide the option to use alternative markup for rendering checkbox and radio button lists. These use the more semantically correct `fieldset` and `legend` elements, instead of the previously used `div` and `label`. - -Although this semantic markup is preferred, it could be a presentational breaking change for those styling the default theme. As such we have made this markup improvement optional. You can opt into using it by setting this configuration value to `true`. - -In Umbraco 13 this configuration option will be removed and the semantic rendering made the only option. - ### DisableClientSideValidationDependencyCheck When a form is rendered on the front-end website, a check is run to ensure that client-side validation framework is available and registered. diff --git a/18/umbraco-forms/developer/configuration/type-details.md b/18/umbraco-forms/developer/configuration/type-details.md index b488f5b5817..6cc1420f30a 100644 --- a/18/umbraco-forms/developer/configuration/type-details.md +++ b/18/umbraco-forms/developer/configuration/type-details.md @@ -296,6 +296,7 @@ The intention is to be able to make available details such as IDs, aliases and p * `Method` * `XsltFile` * `Fields` +* `DefaultElementForFields` * `Username` * `Password` @@ -371,6 +372,9 @@ The intention is to be able to make available details such as IDs, aliases and p * `ReplyToEmail` * `Subject` * `RazorViewFilePath` +* `HeaderHtml` +* `BodyHtml` +* `FooterHtml` * `Attachment` @@ -409,6 +413,7 @@ The intention is to be able to make available details such as IDs, aliases and p * `Method` * `StandardFields` * `Fields` +* `DefaultElementForFields` * `Username` * `Password` diff --git a/18/umbraco-forms/developer/custom-markup.md b/18/umbraco-forms/developer/custom-markup.md index 24e7a28980d..d811d538a66 100644 --- a/18/umbraco-forms/developer/custom-markup.md +++ b/18/umbraco-forms/developer/custom-markup.md @@ -43,20 +43,20 @@ The rest of the views start with FieldType, like `FieldType.Textfield.cshtml` an Contents of the `FieldType.Textfield.cshtml` view (from the default theme): ```csharp -@model Umbraco.Forms.Mvc.Models.FieldViewModel -@using Umbraco.Forms.Mvc - -placeholder="@Model.PlaceholderText"}} - @{if(Model.Mandatory || Model.Validate){data-val="true"}} - @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage"}} - @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)"}} -/> +@using Umbraco.Forms.Web +@model Umbraco.Forms.Web.Models.FieldViewModel +@{ + var maxLength = Model.GetSettingValue("MaximumLength", 255); + var fieldType = Model.GetSettingValue("FieldType", "text"); + var autocompleteAttribute = Model.GetSettingValue("AutocompleteAttribute", string.Empty); +} + placeholder="@Model.PlaceholderText" }} + @{if (string.IsNullOrEmpty(autocompleteAttribute) == false) { autocomplete="@autocompleteAttribute" }} + @{if (Model.Mandatory || Model.Validate) { data-val="true" }} + @{if (Model.Mandatory) { data-val-required="@Model.RequiredErrorMessage" aria-required="true" }} + @{if (Model.Validate) { data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)" }} + aria-describedby="@(Model.Id)_validation@(!string.IsNullOrEmpty(Model.ToolTip) ? $" {Model.Id}_description" : "")"/> ``` Umbraco Forms uses ASP.NET Unobtrusive Validation which is why you see attributes like `data-val` and `data-val-required`. diff --git a/18/umbraco-forms/developer/email-templates.md b/18/umbraco-forms/developer/email-templates.md index b1ad7e8d47f..9b912fa1d62 100644 --- a/18/umbraco-forms/developer/email-templates.md +++ b/18/umbraco-forms/developer/email-templates.md @@ -177,6 +177,7 @@ Below is an example of an email template from the `~/Views/Partials/Forms/Emails { "FieldType.Recaptcha2.cshtml", "FieldType.Recaptcha3.cshtml", + "FieldType.RecaptchaEnterprise.cshtml", "FieldType.RichText.cshtml", "FieldType.Text.cshtml" }; diff --git a/18/umbraco-forms/developer/extending/adding-a-exporttype.md b/18/umbraco-forms/developer/extending/adding-a-exporttype.md index 35e772f181c..583d5fd812a 100644 --- a/18/umbraco-forms/developer/extending/adding-a-exporttype.md +++ b/18/umbraco-forms/developer/extending/adding-a-exporttype.md @@ -23,6 +23,8 @@ In this example below we will create a single HTML file which takes all the subm ```csharp using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Umbraco.Extensions; using Umbraco.Forms.Core; using Umbraco.Forms.Core.Models; using Umbraco.Forms.Core.Searchers; @@ -50,12 +52,11 @@ namespace MyFormsExtensions Icon = "icon-article"; } - public override Task ExportRecordsAsync(Guid formId, RecordExportFilter filter) + public override async Task ExportRecordsAsync(Guid formId, RecordExportFilter filter) { var view = "~/Views/Partials/Forms/Export/html-report.cshtml"; EntrySearchResultCollection model = _formRecordSearcher.QueryDataBase(filter); - return Task.FromResult( - ViewHelper.RenderPartialViewToString(_httpContextAccessor.GetRequiredHttpContext(), view, model)); + return await ViewHelper.RenderPartialViewToString(_httpContextAccessor.GetRequiredHttpContext(), view, model); } } } @@ -91,8 +92,8 @@ namespace MyFormsExtensions ```csharp using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Forms.Core.Providers.Extensions; -using Umbraco.Forms.TestSite.Business.ExportTypes; namespace MyFormsExtensions { @@ -191,7 +192,7 @@ namespace MyFormsExtensions 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(); + 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; diff --git a/18/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md b/18/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md index e7535c9526d..1606fdd4588 100644 --- a/18/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md +++ b/18/umbraco-forms/developer/extending/adding-a-prevaluesourcetype.md @@ -11,6 +11,7 @@ Dynamic settings can be applied and validated as shown in the [Validate type set ```csharp using System; using System.Collections.Generic; +using System.Threading.Tasks; using Umbraco.Forms.Core; using Umbraco.Forms.Core.Models; @@ -25,8 +26,8 @@ namespace MyFormsExtensions Description = "Example prevalue source providing a fixed list of values."; } - public override List GetPreValues(Field field, Form form) => - new List + public override Task> GetPreValuesAsync(Field? field, Form? form) => + Task.FromResult(new List { new PreValue { @@ -40,7 +41,7 @@ namespace MyFormsExtensions Value = "item-two", Caption = "Item Two" } - }; + }); /// public override List ValidateSettings() @@ -85,6 +86,7 @@ This example will take a user-provided Content Node and create a custom Prevalue ```csharp using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models.PublishedContent; @@ -100,7 +102,7 @@ namespace MyFormsExtensions [Umbraco.Forms.Core.Attributes.Setting(name: "Source Node", Alias = "SourceNodeId", Description = "Node holding the Options desired.", - View = "pickers.content")] + View = "Umb.PropertyEditorUi.ContentPicker.Source")] public string SourceNodeId { get; set; } public FormPrevaluesSourceNode( ILogger logger @@ -121,7 +123,7 @@ namespace MyFormsExtensions /// /// /// List of 'Umbraco.Forms.Core.Models.PreValue' - public override List GetPreValues(Field field, Form form) + public override Task> GetPreValuesAsync(Field? field, Form? form) { List result = new List(); try @@ -159,9 +161,9 @@ namespace MyFormsExtensions } catch (Exception ex) { - _logger.LogError($"Unable to get options from FormPrevaluesSourceNode #{SourceNodeId}", ex); + _logger.LogError(ex, "Unable to get options from FormPrevaluesSourceNode #{SourceNodeId}", SourceNodeId); } - return result; + return Task.FromResult(result); } /// /// This is where any checks for Configuration validity are done. diff --git a/18/umbraco-forms/developer/extending/adding-a-type.md b/18/umbraco-forms/developer/extending/adding-a-type.md index c1b1784e3dc..f8368c999f6 100644 --- a/18/umbraco-forms/developer/extending/adding-a-type.md +++ b/18/umbraco-forms/developer/extending/adding-a-type.md @@ -20,7 +20,7 @@ public class LogWorkflow : Umbraco.Forms.Core.WorkflowType _logger = logger; } - public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) + public override Task ExecuteAsync(WorkflowExecutionContext context) { throw new NotImplementedException(); } @@ -31,7 +31,7 @@ public class LogWorkflow : Umbraco.Forms.Core.WorkflowType } ``` -When you implement this class you get two methods added. One of them is Execute which performs the execution of the workflow and the other is a method which validates the workflow settings, we will get back to these settings later on. +When you implement this class you get two methods added. `ExecuteAsync` performs the execution of the workflow. `ValidateSettings` validates the workflow settings, which are covered later in this article. Any dependencies required that are registered with the dependency injection container can be provided via the constructor. @@ -61,7 +61,7 @@ Now that we have a basic class setup, we would like to pass setting items to the ```csharp [Umbraco.Forms.Core.Attributes.Setting("Log Header", Description = "Log item header", - View = "TextField")] + View = "Umb.PropertyEditorUi.TextBox")] public string LogHeader { get; set; } ``` @@ -72,12 +72,12 @@ With the attribute in place, the property value is set every time the class is i ```csharp [Umbraco.Forms.Core.Attributes.Setting("Document ID", Description = "Node the log entry belongs to", - View = "Pickers.Content")] + View = "Umb.PropertyEditorUi.ContentPicker.Source")] public string Document { get; set; } -public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) { +public override Task ExecuteAsync(WorkflowExecutionContext context) { _logger.LogInformation("Record submitted from: {IP}", context.Record.IP); - return WorkflowExecutionStatus.Completed; + return Task.FromResult(WorkflowExecutionStatus.Completed); } ``` @@ -168,7 +168,7 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare // Added a new setting when we add our field to the form [Umbraco.Forms.Core.Attributes.Setting("Max length", Description = "Max length", - View = "TextField")] + View = "Umb.PropertyEditorUi.TextBox")] public string MaxNumberOfChars { get; set; } public TextareaWithCount() @@ -180,9 +180,9 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare this.Name = "Long Answer with Limit"; } - public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, List errors) + public override IEnumerable ValidateField(Form form, Field field, IEnumerable postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, IFieldTypeStorage fieldTypeStorage, List errors) { - var baseValidation = base.ValidateField(form, field, postedValues, context, placeholderParsingService, errors); + var baseValidation = base.ValidateField(form, field, postedValues, context, placeholderParsingService, fieldTypeStorage, errors); var value = postedValues.FirstOrDefault(); if (value != null && value.ToString().Length < int.Parse(MaxNumberOfChars)) @@ -199,7 +199,7 @@ public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textare } ``` -As discussed in the previous section, you must also register the extended field type within a composer. You also need to create the the backoffice field type view. +As discussed in the previous section, you must also register the extended field type within a composer. **Composer:** @@ -213,10 +213,4 @@ public class UmbracoFormsCustomProvidersComposer : IComposer } ``` -**Backoffice View:** - -Add a new HTML file as per the name of the field class (e.g. `textareawithcount.html`) to `\wwwroot\App_Plugins\umbracoforms\Backoffice\Common\FieldTypes\` within your project. For this example, we can copy the original `textarea.html` file used by the standard 'Long Answer' field. - -The AngularJS client-side files are shipped with Umbraco Forms as part of a Razor Class Library. So you won't find these files on disk when you install the package. - -However if you do want to reference them you can view and extract them from the [`Umbraco.Forms.StaticAssets` NuGet package](https://nuget.info/packages/Umbraco.Forms.StaticAssets). +If your custom field type requires a different presentation in the backoffice, you can register client-side components for it. For more information, see the [Adding A Field Type To Umbraco Forms](adding-a-fieldtype.md) article. diff --git a/18/umbraco-forms/developer/extending/adding-a-validation-pattern.md b/18/umbraco-forms/developer/extending/adding-a-validation-pattern.md index b304d9ffc13..6198e354215 100644 --- a/18/umbraco-forms/developer/extending/adding-a-validation-pattern.md +++ b/18/umbraco-forms/developer/extending/adding-a-validation-pattern.md @@ -26,7 +26,7 @@ The following example shows the implementation of a pattern for a United Kingdom ```csharp using Umbraco.Forms.Core.Interfaces; -namespace Umbraco.Forms.TestSite.Business.ValidationPatterns +namespace MyFormsExtensions.ValidationPatterns { public class UkPostCode : IValidationPattern { diff --git a/18/umbraco-forms/developer/extending/adding-a-workflowtype.md b/18/umbraco-forms/developer/extending/adding-a-workflowtype.md index 019f24f030d..bb3975d64ad 100644 --- a/18/umbraco-forms/developer/extending/adding-a-workflowtype.md +++ b/18/umbraco-forms/developer/extending/adding-a-workflowtype.md @@ -5,15 +5,13 @@ Add a new class to your project and have it inherit from `Umbraco.Forms.Core.WorkflowType`, and implement the class. For this sample, we will focus on the execute method. This method processes the current record (the data submitted by the form) and have the ability to change data and state. ```csharp -using Serilog; using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Umbraco.Forms.Core; -using Umbraco.Forms.Core.Data.Storage; using Umbraco.Forms.Core.Enums; using Umbraco.Forms.Core.Persistence.Dtos; -using Microsoft.Extensions.Logging; -using Umbraco.Core.Composing; namespace MyFormsExtensions { diff --git a/18/umbraco-forms/developer/extending/customize-default-workflows.md b/18/umbraco-forms/developer/extending/customize-default-workflows.md index ed47104e6bf..d5830e6ffe8 100644 --- a/18/umbraco-forms/developer/extending/customize-default-workflows.md +++ b/18/umbraco-forms/developer/extending/customize-default-workflows.md @@ -27,11 +27,11 @@ Firstly, the custom workflow: ```csharp using System; using System.Collections.Generic; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Umbraco.Forms.Core; using Umbraco.Forms.Core.Attributes; using Umbraco.Forms.Core.Enums; -using Umbraco.Forms.Core.Persistence.Dtos; namespace MyNamespace { @@ -64,10 +64,10 @@ namespace MyNamespace return exs; } - public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) + public override Task ExecuteAsync(WorkflowExecutionContext context) { - _logger.LogInformation($"'{Message}' written at {DateTime.Now}"); - return WorkflowExecutionStatus.Completed; + _logger.LogInformation("'{Message}' written at {Date}", Message, DateTime.Now); + return Task.FromResult(WorkflowExecutionStatus.Completed); } } } @@ -150,7 +150,6 @@ using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Extensions; using Umbraco.Forms.Core.Providers; -using Umbraco.Forms.Testsite.Business.Workflows; using Umbraco.Forms.Web.Behaviors; namespace MyNamespace diff --git a/18/umbraco-forms/developer/healthchecks/README.md b/18/umbraco-forms/developer/healthchecks/README.md index b7535dec496..0602a9b07ea 100644 --- a/18/umbraco-forms/developer/healthchecks/README.md +++ b/18/umbraco-forms/developer/healthchecks/README.md @@ -113,3 +113,11 @@ To support this, we provide the following SQL scripts: * Revert database integrity schema changes for 8.7.0+ - [8.7.0-apply-keys-and-indexes\_revert](apply-keys.md#revert-application-of-keys-and-indexes) * Revert database integrity schema changes for 8.7.0+ (Forms in database tables) - [8.7.0-apply-keys-and-indexes-forms-in-db\_revert](forms-in-the-database-apply-keys.md#reverting-the-application-of-keys-and-indexes) + +## Analytics Processing Health Check + +Running this health check will verify that the daily summary processing for [Forms analytics](../../editor/analytics.md) is up to date. + +Analytics data is summarized once per day by a background task. The task runs on application startup and on a recurring schedule. If one or more dates have not been processed, analytics data may be incomplete. Queries may also fall back to slower live calculations. + +The health check reports a warning when unprocessed dates are found. The background processing task should resolve this automatically. If the warning persists, check the Umbraco log for errors during analytics processing. diff --git a/18/umbraco-forms/developer/themes.md b/18/umbraco-forms/developer/themes.md index 52f139a4b4d..94caad4da62 100644 --- a/18/umbraco-forms/developer/themes.md +++ b/18/umbraco-forms/developer/themes.md @@ -135,7 +135,14 @@ Files which can be overridden: * Render.cshtml (overrides the entire form - usually not needed) * Form.cshtml (overrides the generation of the fields on the current page) * Script.cshtml (overrides the way files are included with the form) +* ScrollToFormScript.cshtml (overrides the script used to scroll to the form after submission) +* Submitted.cshtml (overrides the message shown when the form is submitted) +* DatePicker.cshtml (overrides the date picker initialization script) +* MultiPageFormPagingDetails.cshtml (overrides the paging details shown on multi-page forms) +* MultiPageFormSummary.cshtml (overrides the summary shown on multi-page forms) * /Fieldtypes/FieldType.\*.cshtml (overrides a specific view for a field) +* /Fieldtypes/FieldType.\*.ReadOnly.cshtml (overrides the read-only view for a field, used on multi-page form summaries) +* /Fieldtypes/ReadOnly.cshtml (overrides the generic read-only view for fields) ## Helper Methods @@ -144,7 +151,7 @@ Files which can be overridden: Sets the primary form theme stylesheet path. This overrides an already assigned stylesheet and will be rendered out when inserting the form into the page ```csharp -Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") +Html.SetFormThemeCssFile("~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css") ``` ### AddFormThemeScriptFile diff --git a/18/umbraco-forms/developer/working-with-data.md b/18/umbraco-forms/developer/working-with-data.md index b5d42a577bf..7d0a24ee17e 100644 --- a/18/umbraco-forms/developer/working-with-data.md +++ b/18/umbraco-forms/developer/working-with-data.md @@ -13,7 +13,7 @@ The methods can be found by injecting the `Umbraco.Forms.Core.Services.IRecordRe ### GetApprovedRecordsFromPage ```csharp -PagedResult GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` Returns all records with the state set to approved from all Forms on the Umbraco page with the id = `pageId` . @@ -21,46 +21,46 @@ Returns all records with the state set to approved from all Forms on the Umbraco ### GetApprovedRecordsFromFormOnPage ```csharp -PagedResult GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records with the state set to approved from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetApprovedRecordsFromForm ```csharp -PagedResult GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize) +PagedModel GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize) ``` -Returns all records with the state set to approved from the Form with the ID = `formId` as a `PagedResult`. +Returns all records with the state set to approved from the Form with the ID = `formId` as a `PagedModel`. ### GetRecordsFromPage ```csharp -PagedResult GetRecordsFromPage(int pageId, int pageNumber, int pageSize) +PagedModel GetRecordsFromPage(int pageId, int pageNumber, int pageSize) ``` -Returns all records from all Forms on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records from all Forms on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetRecordsFromFormOnPage ```csharp -PagedResult GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) +PagedModel GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize) ``` -Returns all records from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedResult`. +Returns all records from the Form with the id = `formId` on the Umbraco page with the id = `pageId` as a `PagedModel`. ### GetRecordsFromForm ```csharp -PagedResult GetRecordsFromForm(Guid formId, int pageNumber, int pageSize) +PagedModel GetRecordsFromForm(Guid formId, int pageNumber, int pageSize) ``` -Returns all records from the Form with the ID = formId as a `PagedResult`. +Returns all records from the Form with the ID = formId as a `PagedModel`. ## The returned objects -All of these methods will return an object of type `PagedResult` so you can iterate through the `Record` objects. +All of these methods will return an object of type `PagedModel` so you can iterate through the `Record` objects. The properties available on a `Record` are: @@ -86,9 +86,8 @@ This extension method handle multi value fields by comma separating the values. Sample script that is outputting comments using a Form created with the default comment Form template. ```csharp -@using Umbraco.Core; -@using Umbraco.Cms.Core.Composing; @using Umbraco.Forms.Core.Extensions; +@using Umbraco.Forms.Core.Services; @inject IRecordReaderService _recordReaderService;
        @@ -133,8 +132,8 @@ Here is a sample code for retrieving a record in a view. Record? record; string submittedEmail; - if (Guid.TryParse(TempData["UmbracoFormSubmitted"]?.ToString(), out Guid formId) && - Guid.TryParse(TempData["Forms_Current_Record_id"]?.ToString(), out Guid recordId)) + if (Guid.TryParse(TempData["UmbracoFormSubmitted"]?.ToString(), out formId) && + Guid.TryParse(TempData["Forms_Current_Record_id"]?.ToString(), out recordId)) { form = _formService.Get(formId); diff --git a/18/umbraco-forms/editor/attaching-workflows/workflow-types.md b/18/umbraco-forms/editor/attaching-workflows/workflow-types.md index 2671a4e64d7..30bee2fb946 100644 --- a/18/umbraco-forms/editor/attaching-workflows/workflow-types.md +++ b/18/umbraco-forms/editor/attaching-workflows/workflow-types.md @@ -28,9 +28,10 @@ Used to post the Form as an XML to a specified URL. The following configuration * Workflow Name * URL (required) -* Method -* XsltFile - used to transform the XML -* Headers - map the needed files +* Method - POST, GET, PUT or DELETE +* XSLT File - used to transform the XML +* Fields - map form fields to values that are sent as HTTP headers with the request +* Default Element For Fields - choose whether the field caption or alias is used as the XML element name * User * Password @@ -158,6 +159,7 @@ Sends the Form to a URL either as a HTTP POST or GET. The following configuratio * Method (required) - POST, GET, PUT or DELETE * Standard Fields - optionally include and map standard form information such as name and page URL * Fields - map the needed fields +* Default Element For Fields - when no fields are mapped, choose whether the field caption or alias is used as the element name * User * Password diff --git a/18/umbraco-forms/upgrading/version-specific.md b/18/umbraco-forms/upgrading/version-specific.md index f2162fb78f4..dffa15e9634 100644 --- a/18/umbraco-forms/upgrading/version-specific.md +++ b/18/umbraco-forms/upgrading/version-specific.md @@ -6,7 +6,7 @@ description: >- # Version Specific Upgrade Notes -This article provides specific upgrade documentation for migrating to Umbraco Forms version 17. +This article provides specific upgrade documentation for migrating to Umbraco Forms version 18. {% hint style="info" %} If you are upgrading to a minor or patch version, you can find the details about the changes in the [Release Notes](../release-notes.md) article. @@ -14,7 +14,7 @@ If you are upgrading to a minor or patch version, you can find the details about ## Version Specific Upgrade Notes History -Version 17 of Umbraco Forms has a minimum dependency on Umbraco CMS core of `17.0.0`. It runs on .NET 9. +Version 18 of Umbraco Forms has a minimum dependency on Umbraco CMS core of `18.0.0`. It runs on .NET 10. ## Legacy version specific upgrade notes