I’ve had similar requirements pop up a few times over the years, and have implemented all of the various options mentioned above, to varying degrees of success.
tl;dr; Don’t make this a content problem. Use a PropertyValueConverter - it has caching built in.
There’s only one safe time/place in the CMS to modify user content - on save. Doing it in the background messes with the history, and means editors don’t see the actual content that they’re publishing.
That would normally be my recommendation, but two things mentioned above don’t sit well with that:
Doing it on save will mean that editors will need to deal with links when they come back and edit. If you don’t want them to have to worry about links at all then you probably don’t want this in content either.
Brand rules change. I used to do a bit of work with LEGO® (Not Lego, lego, or LEGO, but “LEGO®”!) and we had some interesting requirements come in that required us to do things exactly like @dammark’s requirements. What we learnt pretty quickly is that these rules change. You might need to undo any change you make, which isn’t so easy when you’re applying changes to content directly.
Instead, making these requirements a display/viewmodel concern, makes a lot more sense. You don’t need to worry about the content itself and the rules becomes business logic that you can apply to Umbraco’s output rather than mess around with the CMS itself.
Doing this in your own controller and viewmodel is an option, but it takes a fair amount of code to make it performant. Umbraco already has a built in way of transforming content for display - the ConvertIntermediateToObject
method inside of PropertyValueConverter
.
So, I recommend creating custom PropertyValueConverter
s for this. It’s an easy place to transform content for display and comes with caching built in. It also happens after the block grid/list has been constructed from JSON so you only need to worry about actual property values.
You’ll still want to optimise for performance, but really that’s just a case of using native string operations instead of regex.
Overriding the built-in converters requires you to unregister your own (they get typescanned) and pop them back in the right order, like this:
builder.PropertyValueConverters()
.Remove<CustomTextStringValueConverter>()
.Remove<CustomMultipleTextboxValueConverter>()
.Remove<CustomRteValueConverter>()
.InsertBefore<TextStringValueConverter, CustomTextStringValueConverter>()
.InsertBefore<MultipleTextStringValueConverter, CustomMultipleTextboxValueConverter>()
.InsertBefore<RteMacroRenderingValueConverter, CustomRteValueConverter>();
// etc...