Good addresses
Slugs. An address holds plain ASCII only. RelatudeUrlHelper.ToUrlSlug (namespace
Relatude.CMS.Engine.Web) makes a readable one from a name. It lower-cases the name, transliterates
æ, ø and å, removes accents and turns everything else into single dashes:
RelatudeUrlHelper.ToUrlSlug("Blåbær og Øl"); // "blabaer-og-ol"
RelatudeUrlHelper.ToUrlSlug("Café Crème!"); // "cafe-creme"
RelatudeUrlHelper.ToUrlSlug("!!!"); // "node", as nothing is leftCollisions are the store's job. When an address would give two pages the same URL, the store
adds -2, or another number if that is taken. Code that sets an address never checks for
collisions; read the address back if you need it.
Editors change a page's address on its Meta tab in the CMS interface, which moves the page.
In code, use db.UpdateAddress. To read the address as a property, declare a getter-only member
marked [AddressProperty]:
public interface IArticle : IPage {
[AddressProperty] string Slug { get; } // the page's address, read-only
[HtmlProperty] string Body { get; set; }
}Automatically, a transaction plugin can give every new page an address made from its name:
using Relatude.CMS.Engine.Web;
using Relatude.CMS.Models;
using Relatude.DB.Common;
using Relatude.DB.Nodes;
using Relatude.DB.Transactions;
public class PageAddressPlugin : NodeTransactionPlugin<IPage> {
public override void OnBeforeNodeAction(NodeKey key, NodeOperation operation, Transaction transaction, NodeHelper<IPage> helper) {
if (operation is NodeOperation.InsertOrFail or NodeOperation.InsertIfNotExists
&& helper.GetProperty(p => p.Name) is string { Length: > 0 } name) {
transaction.UpdateAddress(key.Guid, RelatudeUrlHelper.ToUrlSlug(name));
}
base.OnBeforeNodeAction(key, operation, transaction, helper);
}
}
// Program.cs
builder.AddRelatudeDB(o => o.OnStoreInit = db => db.RegisterTransactionPlugin(new PageAddressPlugin()));The plugin acts on inserts only, so renaming a page does not move it, and links to it keep working.
It reads the name from the insert, so set the name before you call db.Insert. CreateAndInsert
inserts the page empty and sets its values in a separate update, which this plugin ignores.