Best Way to Transform UI-O-Matic Data (For Better Presentation)?
I've created a redirect dashboard based on Umbraco's umbracoRedirectUrl table, and it works OK:
The only quirk I'm hoping to figure out is that this table stores the root node ID as part of the URL (at least in multitenant sites with multiple domains configured). In this case, "1089" is the root node of one of the domains.
Thanks to the scaffolding event, I can prepopulate this vanity URL with the root node ID of the current domain. However, I'm hoping something cleaner can be created.
What I'm imagining is an extra field, "Domain", that is a drop down containing "site1.com" and "site2.com". And the vanity URL would only display "/your-vanity-url-here". However, when it gets stored, it would combine the domain with the URL to construct the full URL (i.e., "1089/your-vanity-url-here").
Then, when it reads that back from the database, it would set domain based on the ID in the first part of the URL, and it would show the URL without the ID.
Thanks to the ScaffoldingObject, CreatingObject, and UpdatingObject events, I can get halfway to my goal (i.e., I can do a bit of data transformations). However, I'm unsure of the best way to handle the situation of transforming the data for an already existing redirect (there is no LoadingObject event).
How would you go about displaying the domain in a more user friendly way?
If you are curious, here is most of my code:
// Namespaces.
using Routing;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using UIOMatic.Attributes;
using UIOMatic.Enums;
using Umbraco.Core;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
/// <summary>
/// Represents a row in the "umbracoRedirectUrl" table, and is decorated with attributes used
/// to build a CRUD interface in UI-O-Matic.
/// </summary>
[UIOMatic("redirects", "Redirects", "Redirect", FolderIcon = "icon-resize",
ItemIcon = "icon-navigation-horizontal", RenderType = UIOMaticRenderType.List)]
[TableName("umbracoRedirectUrl")]
public class UmbracoRedirect
{
#region Properties
/// <summary>
/// The generated ID of this row.
/// </summary>
[Column("id")]
[PrimaryKeyColumn(AutoIncrement = false)]
public Guid Id { get; set; }
/// <summary>
/// The date this row was created.
/// </summary>
[Column("createDateUtc")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public DateTime CreateDate { get; set; }
/// <summary>
/// The URL to redirect away from.
/// </summary>
[UIOMaticField(View = UIOMatic.Constants.FieldEditors.Textfield, Name = "Vanity URL",
Description = "The URL you want to redirect away from. Note that this URL must start with the ID of a root content node.")]
[UIOMaticListViewField(Name = "Vanity URL")]
[Required]
[Column("url")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public string Url { get; set; }
/// <summary>
/// The GUID of the Umbraco content node to redirect to.
/// </summary>
[UIOMaticField(View = "~/App_Plugins/Rhythm.UIOMatic/editors/pickers.content.guid.html",
Name = "Destination Page", Description = "Pick the page to redirect to.")]
[Column("contentKey")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public Guid ContentKey { get; set; }
/// <summary>
/// The hash of the URL (used for performance reasons).
/// </summary>
[Column("urlHash")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Length(40)]
public string UrlHash { get; set; }
#endregion
#region Public Methods
/// <summary>
/// handles a redirect that is being scaffolded.
/// </summary>
/// <param name="redirect">
/// The redirect being scaffolded.
/// </param>
public static void HandleScaffold(UmbracoRedirect redirect)
{
redirect.Url = "your-vanity-url-here";
EnsureStartsWithNodeId(redirect);
redirect.Id = Guid.NewGuid();
}
/// <summary>
/// Handles a redirect that is being created.
/// </summary>
/// <param name="redirect">
/// The redirect being created.
/// </param>
public static void HandleCreate(UmbracoRedirect redirect)
{
EnsureStartsWithNodeId(redirect);
redirect.CreateDate = DateTime.UtcNow;
redirect.UrlHash = redirect.Url.ToSHA1();
}
/// <summary>
/// Handles an update to a redirect.
/// </summary>
/// <param name="redirect">
/// The redirect being updated.
/// </param>
public static void HandleUpdate(UmbracoRedirect redirect)
{
EnsureStartsWithNodeId(redirect);
redirect.UrlHash = redirect.Url.ToSHA1();
}
#endregion
#region Private Methods
/// <summary>
/// Ensures the specified redirect URL begins with a root node ID.
/// </summary>
/// <param name="redirect">
/// The redirect.
/// </param>
private static void EnsureStartsWithNodeId(UmbracoRedirect redirect)
{
// Validate input.
if (redirect.Url.StartsWith("/"))
{
redirect.Url = redirect.Url.TrimStart("/");
}
if (Regex.IsMatch(redirect.Url, @"^[0-9]+/.+"))
{
return;
}
// Get the ID for the root node for the current domain.
var contentService = ApplicationContext.Current.Services.ContentService;
var domain = HttpContext.Current.Request.Url.Host;
var regionalHomepage = RoutingHelper.GetAllHomepages()
.Where(x => domain.InvariantEquals(x.Domain)).FirstOrDefault();
var domainNodeId = contentService.GetById(regionalHomepage.NodeId).ParentId;
// Prefix the node ID.
redirect.Url = $"{domainNodeId}/{redirect.Url}";
}
#endregion
#region Overridden Methods
/// <summary>
/// A string representation of this redirect (potentially used to display in the
/// UI-O-Matic tree, though currently this table is configured to display as a list).
/// </summary>
/// <returns>
/// A truncated URL.
/// </returns>
public override string ToString()
{
var partialUrl = Url ?? "/";
if (partialUrl.Length > 40)
{
var firstPart = partialUrl.Substring(0, 20);
var lastPart = partialUrl.Substring(partialUrl.Length - 20);
partialUrl = $"{firstPart}…{lastPart}";
}
return partialUrl;
}
#endregion
}
Best Way to Transform UI-O-Matic Data (For Better Presentation)?
I've created a redirect dashboard based on Umbraco's
umbracoRedirectUrl
table, and it works OK:The only quirk I'm hoping to figure out is that this table stores the root node ID as part of the URL (at least in multitenant sites with multiple domains configured). In this case, "1089" is the root node of one of the domains.
Thanks to the scaffolding event, I can prepopulate this vanity URL with the root node ID of the current domain. However, I'm hoping something cleaner can be created.
What I'm imagining is an extra field, "Domain", that is a drop down containing "site1.com" and "site2.com". And the vanity URL would only display "/your-vanity-url-here". However, when it gets stored, it would combine the domain with the URL to construct the full URL (i.e., "1089/your-vanity-url-here").
Then, when it reads that back from the database, it would set domain based on the ID in the first part of the URL, and it would show the URL without the ID.
Thanks to the
ScaffoldingObject
,CreatingObject
, andUpdatingObject
events, I can get halfway to my goal (i.e., I can do a bit of data transformations). However, I'm unsure of the best way to handle the situation of transforming the data for an already existing redirect (there is noLoadingObject
event).How would you go about displaying the domain in a more user friendly way?
If you are curious, here is most of my code:
is working on a reply...