<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Busra Sengul</title>
    <description>The latest articles on DEV Community by Busra Sengul (@busrasengul).</description>
    <link>https://dev.to/busrasengul</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F766767%2F80fa0bd4-c029-4b72-a59f-d1225041521d.jpeg</url>
      <title>DEV Community: Busra Sengul</title>
      <link>https://dev.to/busrasengul</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/busrasengul"/>
    <language>en</language>
    <item>
      <title>Deleting Umbraco Forms(13) Entries via SQL</title>
      <dc:creator>Busra Sengul</dc:creator>
      <pubDate>Tue, 27 May 2025 13:24:58 +0000</pubDate>
      <link>https://dev.to/busrasengul/deleting-umbraco-forms-entries-via-sql-597f</link>
      <guid>https://dev.to/busrasengul/deleting-umbraco-forms-entries-via-sql-597f</guid>
      <description>&lt;p&gt;When working with Umbraco Forms, it's common to accumulate a large number of form submissions over time — especially on high-traffic sites. While Umbraco provides a UI to manage entries, bulk deletion (especially based on date) isn't available out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Scheduled Deletion?
&lt;/h2&gt;

&lt;p&gt;Umbraco Forms does include a scheduled record deletion feature, which allows you to configure automatic cleanup of form entries after a specified number of days. This is a great option for routine maintenance, especially when you want to apply a blanket policy across all forms.&lt;/p&gt;

&lt;p&gt;However, there are scenarios where more granular control is needed — for example, deleting entries from specific forms, targeting a custom date range, or integrating cleanup into a broader data management workflow. In such cases, a custom SQL-based approach offers the flexibility and precision required.&lt;/p&gt;

&lt;p&gt;In this post, we’ll walk through how to safely delete form entries for Umbraco Forms 13, before a specific date using SQL, while maintaining referential integrity across the related tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Umbraco Forms Data Structure
&lt;/h2&gt;

&lt;p&gt;Umbraco Forms stores form submissions across several tables:&lt;/p&gt;

&lt;p&gt;UFRecords: Each form submission (entry) is stored here.&lt;br&gt;
UFRecordFields: Links each field in a form to a specific submission.&lt;br&gt;
UFRecordAudit: This table logs changes made to form entries via the Umbraco backoffice. It helps track who edited what and when — useful for auditing and compliance.&lt;br&gt;
UFRecordDataXXX: Stores the actual field values, split by data type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UFRecordDataString&lt;/li&gt;
&lt;li&gt;UFRecordDataLongString&lt;/li&gt;
&lt;li&gt;UFRecordDataInteger&lt;/li&gt;
&lt;li&gt;UFRecordDataBit&lt;/li&gt;
&lt;li&gt;UFRecordDataDateTime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each UFRecordDataXXX table uses a Key column (a GUID) that maps to the Id of a record in UFRecordFields.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;To delete a form entry, you must:&lt;/p&gt;

&lt;p&gt;Remove all related field values from the UFRecordDataXXX tables.&lt;br&gt;
Remove the corresponding rows from UFRecordFields.&lt;br&gt;
Finally, delete the entry from UFRecords.&lt;br&gt;
Failing to follow this order can lead to orphaned data or foreign key constraint errors.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Solution: SQL Script for Safe Deletion
&lt;/h2&gt;

&lt;p&gt;Here’s the SQL script we used to delete all entries created before a specific date (e.g., 2024-01-01):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Set the cutoff date
DECLARE @CutoffDate DATETIME = '2024-01-01';

-- Step 1: Delete from UFRecordDataXXX tables using JOINs
DELETE ds
FROM UFRecordDataString ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate;

DELETE ds
FROM UFRecordDataLongString ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate;

DELETE ds
FROM UFRecordDataInteger ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate;

DELETE ds
FROM UFRecordDataBit ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate;

DELETE ds
FROM UFRecordDataDateTime ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate;


-- Step 2: Delete from UFRecordAudit
DELETE a
FROM UFRecordAudit a
JOIN UFRecords r ON a.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate;

-- Step 3: Delete from UFRecordFields
DELETE f
FROM UFRecordFields f
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate;

-- Step 4: Delete from UFRecords
DELETE FROM UFRecords
WHERE Created &amp;lt; @CutoffDate;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Targeting a Specific Form
&lt;/h2&gt;

&lt;p&gt;If you only want to delete entries for a specific form, you’ll need to filter by the form’s unique identifier (GUID), which is stored in the Form column of the UFRecords table.&lt;/p&gt;

&lt;p&gt;You can find the form’s GUID in the Umbraco backoffice or by querying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT DISTINCT [Key], [Name] FROM UFForms;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have the form’s GUID, you can modify the SQL script like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Example: Only delete entries for a specific form
DECLARE @CutoffDate DATETIME = '2024-01-01';
DECLARE @FormId UNIQUEIDENTIFIER = 'YOUR-FORM-GUID-HERE';

-- Step 1: Delete from UFRecordDataXXX tables
DELETE ds
FROM UFRecordDataString ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);

-- Repeat for other UFRecordDataXXX tables...
DELETE ds
FROM UFRecordDataString ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);

DELETE ds
FROM UFRecordDataLongString ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);

DELETE ds
FROM UFRecordDataInteger ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);

DELETE ds
FROM UFRecordDataBit ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);

DELETE ds
FROM UFRecordDataDateTime ds
JOIN UFRecordFields f ON ds.[Key] = f.[Key]
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);


-- Step 2: Delete from UFRecordAudit
DELETE a
FROM UFRecordAudit a
JOIN UFRecords r ON a.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);

-- Step 3: Delete from UFRecordFields
DELETE f
FROM UFRecordFields f
JOIN UFRecords r ON f.Record = r.Id
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);

-- Step 4: Delete from UFRecords
DELETE FROM UFRecords
WHERE r.Created &amp;lt; @CutoffDate And (@FormId IS NULL or r.Form = @FormId);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach gives you precise control over which form’s data is deleted — ideal for scenarios where different forms have different retention policies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety Tips
&lt;/h2&gt;

&lt;p&gt;Always back up your database before running deletion scripts.&lt;br&gt;
Test the script in a local/dev environment first.&lt;br&gt;
Consider wrapping this logic in a stored procedure or SQL Agent job for scheduled cleanups.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bonus: Make It Reusable
&lt;/h2&gt;

&lt;p&gt;You can easily convert this into a stored procedure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE PROCEDURE DeleteOldUmbracoFormEntries
    @CutoffDate DATETIME,
    @FormId UNIQUEIDENTIFIER = NULL
AS
BEGIN
    -- [Insert the full script here]
END
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Cleaning up old form entries in Umbraco Forms can help improve performance and reduce storage overhead. With a clear understanding of the data structure and a safe deletion strategy, you can confidently manage your form data at scale.&lt;/p&gt;

&lt;p&gt;Hope this helps to someone :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Managing Stale Carts in Umbraco Commerce</title>
      <dc:creator>Busra Sengul</dc:creator>
      <pubDate>Wed, 30 Oct 2024 11:12:39 +0000</pubDate>
      <link>https://dev.to/busrasengul/managing-stale-carts-in-umbraco-commerce-2092</link>
      <guid>https://dev.to/busrasengul/managing-stale-carts-in-umbraco-commerce-2092</guid>
      <description>&lt;p&gt;If you have an e-commerce site using Umbraco Commerce that handles a lot of traffic, you might notice an accumulation of carts that never convert into orders. This was the case for my site, which has been running for about two years and had thousands of stale carts!&lt;/p&gt;

&lt;p&gt;Adding to the complexity, I migrated this site from Vendr to Umbraco Commerce. If you’re considering a similar migration, you can refer to the &lt;a href="https://docs.umbraco.com/umbraco-commerce/upgrading/migrate-from-vendr-to-umbraco-commerce" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Post-migration, a background job was set up to export all orders to a CRM. However, the stale carts were causing issues unrelated to the migration but due to other development factors. Our definition of product changed quite a bit, causing lots of errors being thrown due to 'cart' no longer being able to identify 'product' anymore. It would have also meant customers would have 'old' products in their cart that they could no longer order, therefore, clearing them all out was the best approach.&lt;/p&gt;

&lt;p&gt;Some people suggested deleting them manually through the CMS, selecting 30 at a time, which would have been very time-consuming.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foiy91v8xc3oue9f1s3o5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foiy91v8xc3oue9f1s3o5.png" alt="Image description" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After extensive research, I found &lt;a href="https://github.com/umbraco/Umbraco.Commerce.Issues/issues/420#issuecomment-1603910479" rel="noopener noreferrer"&gt;this issue&lt;/a&gt; and decided to swap Vendr tables with Umbraco Commerce tables. Here’s the SQL query I used to delete the stale carts efficiently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN TRANSACTION;

-- Select all carts before date
SELECT * INTO #tempOrders FROM umbracoCommerceOrder 
WHERE createDate &amp;lt; 'yyyy/mm/dd' AND (finalizedDate IS NULL)

-- Delete data referenced to orders above in related tables
DELETE FROM umbracoCommerceFrozenPrice 
WHERE umbracoCommerceFrozenPrice.OrderId IN (SELECT Id FROM #tempOrders);

DELETE FROM umbracoCommerceOrderPriceAdjustment 
WHERE umbracoCommerceOrderPriceAdjustment.OrderId IN (SELECT Id FROM #tempOrders);

DELETE FROM umbracoCommerceOrderAmountAdjustment 
WHERE umbracoCommerceOrderAmountAdjustment.OrderId IN (SELECT Id FROM #tempOrders);

DELETE FROM umbracoCommerceOrderAppliedDiscountCode 
WHERE umbracoCommerceOrderAppliedDiscountCode.OrderId IN (SELECT Id FROM #tempOrders);

DELETE FROM umbracoCommerceOrderAppliedGiftCard 
WHERE umbracoCommerceOrderAppliedGiftCard.OrderId IN (SELECT Id FROM #tempOrders);

DELETE FROM umbracoCommerceOrderLineAttribute 
WHERE umbracoCommerceOrderLineAttribute.OrderLineId IN (SELECT Id FROM umbracoCommerceOrderLine WHERE OrderId IN (SELECT Id FROM #tempOrders));

DELETE FROM umbracoCommerceOrderLineProperty 
WHERE umbracoCommerceOrderLineProperty.OrderLineId IN (SELECT Id FROM umbracoCommerceOrderLine WHERE OrderId IN (SELECT Id FROM #tempOrders));

DELETE FROM umbracoCommerceOrderProperty 
WHERE umbracoCommerceOrderProperty.OrderId IN (SELECT Id FROM #tempOrders);

DELETE FROM umbracoCommerceOrderLine 
WHERE umbracoCommerceOrderLine.OrderId IN (SELECT Id FROM #tempOrders) AND umbracoCommerceOrderLine.parentOrderLineId IS NOT NULL;

DELETE FROM umbracoCommerceOrderLine 
WHERE umbracoCommerceOrderLine.OrderId IN (SELECT Id FROM #tempOrders);

DELETE FROM umbracoCommerceOrder 
WHERE umbracoCommerceOrder.Id IN (SELECT Id FROM #tempOrders);

-- Drop the temporary table
DROP TABLE #tempOrders;

-- Use ROLLBACK for testing
-- ROLLBACK;

-- Use COMMIT for deleting
COMMIT;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach allowed me to clean up the stale carts quickly and efficiently.&lt;/p&gt;

&lt;p&gt;I hope this helps! &lt;br&gt;
B&lt;/p&gt;

</description>
      <category>umbracocommerce</category>
      <category>umbraco</category>
      <category>sql</category>
      <category>database</category>
    </item>
    <item>
      <title>Umbraco Forms 12+, Custom Field Type</title>
      <dc:creator>Busra Sengul</dc:creator>
      <pubDate>Thu, 17 Oct 2024 16:46:22 +0000</pubDate>
      <link>https://dev.to/busrasengul/umbraco-forms-12-custom-field-type-4dfc</link>
      <guid>https://dev.to/busrasengul/umbraco-forms-12-custom-field-type-4dfc</guid>
      <description>&lt;p&gt;This is an addition to &lt;a href="https://dev.to/busrasengul/umbraco-forms-12-custom-workflow-with-using-restclient-36j3"&gt;my previous post&lt;/a&gt; about Umbraco Forms. &lt;/p&gt;

&lt;p&gt;This time let's create a custom field!&lt;/p&gt;

&lt;p&gt;Sometimes we need fields that are not visually rendered in the FE, but the FE still needs to pass those values on a form submission. I'm sure you can instantly think of a few use cases for this.&lt;/p&gt;

&lt;p&gt;In my case, I needed to have a text string field for the address, and then FE was responsible for having a postcode look up and sending back 5 different fields to the BE and then to a custom workflow. &lt;/p&gt;

&lt;p&gt;Let's dive right into how with steps;&lt;/p&gt;

&lt;p&gt;1-Install Umbraco Forms for your Umbraco 12+ project&lt;br&gt;
dotnet add package Umbraco.Forms&lt;/p&gt;

&lt;p&gt;You'll need to buy your license for your domain when you go live!&lt;/p&gt;

&lt;p&gt;2-Create a custom field type;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class AddressLookupField : FieldType
{
    public AddressLookupField()
    {
        Id = new Guid("2e26a579-2859-44fa-8778-fcdab81675d2");

        Alias = "addressLookup";

        Name = "Address Lookup";

        Description = "Address Lookup";

        Icon = "icon-terminal";

        DataType = FieldDataType.String;

        FieldTypeViewName = "FieldType.AddressLookupField.cshtml";

        SortOrder = 10;

        SupportsRegex = true;
    }

    public override IEnumerable&amp;lt;string&amp;gt; ValidateField(Form form, Field field, IEnumerable&amp;lt;object&amp;gt; postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, IFieldTypeStorage fieldTypeStorage)
    {
        var returnStrings = new List&amp;lt;string&amp;gt;();

        return base.ValidateField(form, field, postedValues, context, placeholderParsingService, fieldTypeStorage, returnStrings);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class is for backoffice to understand there's a new field type to be added to the forms field types.&lt;br&gt;
Here you can see it's now available as an answer type&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6wn2g806u8n7da1s7o6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6wn2g806u8n7da1s7o6.png" alt="Image description" width="131" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3-We also need a .html file to display this field in the back office;&lt;br&gt;
Add the following code to the path;&lt;br&gt;
&lt;code&gt;App_Plugins/UmbracoForms/backoffice/Common/FieldTypes/addresslookupfield.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input type="text" tabindex="-1"
       class="input-block-level"
       style="max-width: 300px" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can see the field too&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80dl05wjz2zz9ajs1mis.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80dl05wjz2zz9ajs1mis.png" alt="Image description" width="785" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4-Create a .cshtml file to&lt;br&gt;
&lt;code&gt;Views/Partials/Forms/themes/customProjectThemeFolder/FieldTypes/FieldType.AddressLookupField.cshtml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here's the .cshtml will look like;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@model Umbraco.Forms.Web.Models.FieldViewModel
@using Umbraco.Forms.Web

&amp;lt;input type="text" name="addressLookup" id="addressLookup" class="text" value="@Model.ValueAsHtmlString" maxlength="500" /&amp;gt;

&amp;lt;input type="hidden" name="addressLine1" id="addressLine1" class="text" value="@Model.ValueAsHtmlString" maxlength="500" /&amp;gt;

&amp;lt;input type="hidden" name="addressLine2" id="addressLine2" class="text" value="@Model.ValueAsHtmlString" maxlength="500" /&amp;gt;

&amp;lt;input type="hidden" name="city" id="city" class="text" value="@Model.ValueAsHtmlString" maxlength="500" /&amp;gt;

&amp;lt;input type="hidden" name="postcode" id="postcode" class="text" value="@Model.ValueAsHtmlString" maxlength="500" /&amp;gt;

&amp;lt;input type="hidden" name="country" id="country" class="text" value="@Model.ValueAsHtmlString" maxlength="500" /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we only display the actual addressLookup field and there are 5 more additional fields that are hidden.&lt;/p&gt;

&lt;p&gt;This is how you create a custom field!&lt;/p&gt;

&lt;p&gt;Let's use this on a workflow;&lt;/p&gt;

&lt;p&gt;When a form is submitted and you get your record fields by;&lt;br&gt;
&lt;code&gt;records.GetRecordFieldByAlias("addressLookup")?.Values;&lt;/code&gt; you get an array of the fields you had hidden!&lt;/p&gt;

&lt;p&gt;Here's what my payload looks like&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb1say48n2ddwmkjbt33j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb1say48n2ddwmkjbt33j.png" alt="Image description" width="712" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And Umbraco forms are smart enough to pick it up in the submissions!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodxsjpny336oxtd6d4q6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodxsjpny336oxtd6d4q6.png" alt="Image description" width="651" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope this helps someone!&lt;br&gt;
B&lt;/p&gt;

</description>
      <category>umbraco</category>
      <category>dotnet</category>
      <category>umbracoforms</category>
      <category>programming</category>
    </item>
    <item>
      <title>Microsoft Clarity in Azure AD B2C</title>
      <dc:creator>Busra Sengul</dc:creator>
      <pubDate>Fri, 01 Mar 2024 15:33:54 +0000</pubDate>
      <link>https://dev.to/busrasengul/microsoft-clarity-in-azure-ad-b2c-5f82</link>
      <guid>https://dev.to/busrasengul/microsoft-clarity-in-azure-ad-b2c-5f82</guid>
      <description>&lt;p&gt;Have you heard about Microsoft Clarity before?&lt;/p&gt;

&lt;p&gt;Microsoft's official definition is &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Clarity is a free user behavior analytics tool that helps you understand how users are interacting with your website through session replays and heatmaps.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What about Azure AD B2C?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Azure AD B2C is a customer identity access management (CIAM) solution capable of supporting millions of users and billions of authentications per day. It takes care of the scaling and safety of the authentication platform, monitoring, and automatically handling threats like denial-of-service, password spray, or brute force attacks.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been growing quite fond of Clarity recently! It's an amazing tool and will always be free! Super simple to get started as well;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add your site to &lt;a href="https://clarity.microsoft.com/"&gt;https://clarity.microsoft.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Get the tracking script of your choice&lt;/li&gt;
&lt;li&gt;Put the script into the head element of your view page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far so good. &lt;/p&gt;

&lt;p&gt;I also wanted to add this script to a user flow we had in place including Azure B2C as the login process.&lt;/p&gt;

&lt;p&gt;Here are some of the contexts you need to be aware of before we start how to achieved this.&lt;/p&gt;

&lt;p&gt;The project uses Sign In user flow, as well as one of the default page layouts that Azure B2C offers, with nothing special within either. Configuration is detailed &lt;a href="https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-user-flow"&gt;here&lt;/a&gt;, and pick a template after creating your user flow. Check the &lt;a href="https://learn.microsoft.com/en-us/azure/active-directory-b2c/customize-ui?pivots=b2c-user-flow"&gt;documentation&lt;/a&gt; on how to do it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foczjr4mqeixjifkyjxo0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foczjr4mqeixjifkyjxo0.png" alt="Default templates in the dropdown" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While we want to keep the template, we also want to inject the Clarity script into these pages that have been generated for us. &lt;/p&gt;

&lt;h2&gt;
  
  
  Here are the steps to achieve this;
&lt;/h2&gt;

&lt;p&gt;1- Enable JavaScript enforcing&lt;br&gt;
Go to the properties of your user flow, enable the JavaScript enforcing&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxe50x7qrg2gov9652jco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxe50x7qrg2gov9652jco.png" alt="JavaScript enabling" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2- Download the default page layout template you want to keep using&lt;br&gt;
&lt;a href="https://github.com/Azure-Samples/Azure-AD-B2C-page-templates"&gt;Here&lt;/a&gt; is the GitHub repo for the default page layout templates&lt;/p&gt;

&lt;p&gt;3- Edit the relevant HTML page by adding your script to the head element of your template, if you do not need to, you do not have to touch the rest of the page!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjlc5wzdo412qj0x4kkhe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjlc5wzdo412qj0x4kkhe.png" alt="Script in the HTML page" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4- Now host these wanted HTML files somewhere. Can be in blob storage, can be within your code, or wherever you want them to be.&lt;br&gt;
For the sake of this article, I'm going to share the approach Microsoft Docs suggested, &lt;a href="https://learn.microsoft.com/en-us/azure/active-directory-b2c/customize-ui-with-html?pivots=b2c-user-flow#2-create-an-azure-blob-storage-account"&gt;hosting with an Azure Blob Storage&lt;/a&gt;&lt;br&gt;
If you're choosing to host the HTML files in the blob storage do not forget to configure CORS!&lt;/p&gt;

&lt;p&gt;5- Finally update your user flow page layouts with the blob storage hosted HTML file URLs!&lt;/p&gt;

&lt;p&gt;There it is! It might take a little while for tracking to kick in, but you now can see your B2C steps in the Clarity!&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>clarity</category>
      <category>microsoftclarity</category>
      <category>b2c</category>
    </item>
    <item>
      <title>Umbraco Forms 12, Custom Workflow with using RestClient</title>
      <dc:creator>Busra Sengul</dc:creator>
      <pubDate>Mon, 04 Dec 2023 11:52:03 +0000</pubDate>
      <link>https://dev.to/busrasengul/umbraco-forms-12-custom-workflow-with-using-restclient-36j3</link>
      <guid>https://dev.to/busrasengul/umbraco-forms-12-custom-workflow-with-using-restclient-36j3</guid>
      <description>&lt;p&gt;I recently needed a custom workflow for an Umbraco Cloud project. My Custom Workflow needed to send some form details using a third-party API. I achieved this using the rest client on submission. &lt;br&gt;
Here are the 3 steps to make this possible on an Umbraco 12+ project!&lt;/p&gt;

&lt;p&gt;1- Install Umbraco Forms for your Umbraco 12+ project&lt;br&gt;
&lt;code&gt;dotnet add package Umbraco.Forms&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You'll need to buy your license for your domain when you go live!&lt;/p&gt;

&lt;p&gt;2- Add a new class for your custom workflow and inherit WorkflowType&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace MySite.FormExtensions
{
    public class CustomWorkflow : WorkflowType
    {
        //We need to read our API keys from secrets/appconfig.json
        private readonly IConfiguration _configuration;

        public CustomWorkflow (IConfiguration configuration)
        {
            _configuration = configuration;
            Id = new Guid("D6A2C01-CF50-11DE-B075-55B055D75689");
            Name = "Custom workflow name";
            Description = "Custom workflow description";
            Icon = "icon-terminal";
            Group = "Services";
        }

        public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context)
        {
            //Get your form information from here
            foreach (RecordField rf in context.Record.RecordFields.Values)
            {
                // and we can then do something with the collection of values on each field
                List&amp;lt;object&amp;gt; vals = rf.Values;

                // or get it as a string
                rf.ValuesAsString(false);
            }

            //or get them one by one
            var fieldName = context.Record.GetRecordFieldByAlias("fieldAlias")?.Values;

            var options = new RestClientOptions(_configuration["Rest:Api:BaseUrl"]);

            options.Authenticator = new HttpBasicAuthenticator(_configuration["Rest:Api:Username"], _configuration["Rest:Api:Password"]);

            var client = new RestClient(options);

            var request = new RestRequest("", Method.Post);

            request.AddHeader("accept", "application/json");

            request.AddHeader("content-type", "application/json");

            request.AddHeader("authorization", "Basic " + options.Authenticator);

            request.AddJsonBody(vals.ToJson(), false);

            var response = client.PostAsync(request);

            context.Record.State = FormState.Approved;

            return WorkflowExecutionStatus.Completed;
        }

        public override List&amp;lt;Exception&amp;gt; ValidateSettings()
        {
            return new List&amp;lt;Exception&amp;gt;();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- Register your custom workflow class&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CustomWorkflowComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        builder.AddFormsWorkflow&amp;lt;CustomWorkflow&amp;gt;();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your custom workflow to your form's workflow now, either on the submission step or on approval. &lt;/p&gt;

&lt;p&gt;The only thing to be aware of here is how your third party wants your data. Mine asked for JSON format hence the line &lt;br&gt;
&lt;code&gt;request.AddJsonBody(vals.ToJson(), false);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it :)&lt;br&gt;
Hope this will be useful for you! &lt;br&gt;
B&lt;/p&gt;

</description>
      <category>umbraco</category>
      <category>umbracoforms</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
