Relatude Help

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 left

Collisions 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.