Not a member yet?

Not a member yet? Register now!

*
*
*

*
Already a member?

New in 5.5: Macro improvements

   —   
There was a lot of macro parameters in 5.0 already, so having twice as much in 5.5 seems almost unbelievable. Let's see what is new ...
Hi there,

As I noted in one of my previous posts, the macros belong to the part of the development where there is no exact plan what will be available in new version. The new options also depend on what we need for our new functionality and how much I can cover in my spare time (in case you don't need too much assistance and consulting ;-) ).

There is a lot of things done in 5.5 macros and sice standard documentation doesn't cover them (since it is often a last minute development), so I typically cover such things here. Let's see what has changed and what is new ...

Processing of the macro parameters

One major change was done on the processing side of the macros. In 5.0 and sooner, the macro parameters were pretty much just flags applied at the end of macro processing. So for example conversion was always done before equals, etc, the order of operation was basically hardcoded. With this, it also didn't make sense to use one parameter multiple times. The processing was done this way:
  1. Get the value selector
  2. Parse parameters and set flags
  3. Get the base value
  4. Apply the flagged parameters in the hardcoded order
  5. Do some post processing (handle SQL injection, encoding, recursion)
Yes, it was absolutely dummy, I totally agree with you. There actually was no need to make it smarter at that time.

5.5 is much better, providing macro resolving in form of state machine. This was basically done due to requirements of some new parameters, and also to provide better options in future (let's face it, macro engine is becoming so powerful that I already almost believe you will be able to make a page completely resolved as a macro :-) ) Let's see how the processing is done, that will give you the best picture:
  1. Get the value selector
  2. Parse the parameters and save them as a list
  3. Get the base value
  4. Apply parameters in the original order one by one, reusing result
  5. Do some post processing (handle SQL injection, encoding, recursion)
So the main improvements are having the list of parameters applied as they were put there. It also means that the same parameter can be applied several times as you process the macro value further and further. And the other thing is that macro resolver now internally used result in form of object, previously, it did all operations on the strings. This will mainly have great impact on macros in 6.0, which really will be mindblowing, trust me, we already have some prototypes being able to do unbelievable stuff.

New macro selectors

There are several nice macro selectors giving empty or constant values:
  • {%EmptyString%} - Returns String.Empty
  • {%Zero%} - Returns 0
  • {%Pi%} - Returns Math.PI
They can be used for various reasons, such as defining constants like this: {%Zero|(add)5%}

New mathematical operations

First of all, there are some improvement on the computational availability of the macros (if you need to make some more advanced calculations for some value):

|(divide)<value> - Is pretty much self-explanatory, it divides the value by the given number. Prior to this, you would have to multiply by 0.333333333... now it gets much easier with {%Something|(divide)3%}.

|(sin), |(cos), |(tan), |(sqrt) - Does corresponding goniometrial function with the result, you use it like {%Something|(sin)%}

|(pow)<value> - Computes base raised to given value exponent, use it like: {%Something|(pow)2%} which means Something2

New string manipulation

String manipulation functions are much more interesting, expanding the options to manipulate string the ways you can use in C#:

|(append)<string> - Apppends (suffixes) the given string to the value, e.g. {%Zero|(append)ABC%} returns 0ABC

|(prepend)string> - Prepends (prefixes) the given string to the value, e.g. {%Zero|(prepend)ABC%} returns ABC0

|(trim) - Removes the white spaces on the beginning and end of the value

|(trim)<chars> - Removes the given set of characters on the beginning and end of the value, e.g.

{%EmptyString|(append)ABCD|(trim)AD%} returns BC

|(trimend), |(trimend)<chars> - Does the trimming only on the end of the string

|(trimstart), |(trimstart)<chars> - Does the trimming only on the end of the string

|(padleft)<total width> - Makes sure that the string has total width of characters, filling the blanks with space

|(padleft)<total width>(with)<char> - Same as previous, but you can specify the filling character, so

{%Zero|(add)5|(padleft)3(with)0%} gives you 005

|(padright)<total width>, |(padright)<total width>(with)<char> - Same as previous, but with filling on the right side

|(substring)<start> - Gets the part of the string after the given index

|(substring)<start>;<length> - Gets the part of the string, e.g. {%CurrentDocument.DocumentName|(substring)1;3|(user)katerinap|(hash)3b916f4379f21ea907113f94a611003ee87604a4d6fc14cebed831418dfa2b8d%} on document "News" will give you "ews"

|(replace)<src>(with)<dest> - Replaces the src string with the destination string so when you do {%CurrentDocument.DocumentName|(replace)e(with)er|(user)katerinap|(hash)40c737224b44316adbce9ced38d14b67876820845d50fc6fab645c6b97744d8a%} on "Home", it will give you "Homer"

|(getmatch)<regex> - Does a regex match and returns the matched part, so

{%CurrentDocument.DocumentName|(getmatch)[k-r]|(user)katerinap|(hash)2f3cfe87046597ad9abe329bdc6cb269f5af6f540f53039f0b4b989d075fa509%} will given you "om" on "Home"

|(regexreplace)<regex>(with)<dest> - Similar to replace, but uses regular expression replace for evaluation.

|(striptags) - Calls HTMLHelper.StripTags on result, leaving only the text.

|(limitlength)<chars> - Calls TextHelper.LimitLength making your value having some maximum length with ellipsis

|(resolveurl) - Resolves the virtual path or URL, e.g. converts ~/Home.aspx to /KenticoCMS/Home.aspx, based on context

|(unresolveurl) - Unresolves the URL, based on the context, does the opposite as previous

|(mappath) - Maps the virtual path to the physical path, e.g. ~/Home.aspx to C:\Inetpub\wwwroot\KenticoCMS\Home.aspx

|(jsescape) - Does the escape for the value of javascript string (handles quotes and \n as escaped)

|(sqlescape) - Does the escape on current value to prevent SQL injection (replaces ' with '')

|(clear) - Clears current value to produce empty result (you will see why later)

New comparison options

Comparison (evaluation to boolean value) has also some new options. They sure all support truevalue / falsevalue parameters.

|(startswith)<string> - True if value starts with given string

|(endswith)<string> - True if value ends with given string

|(contains)<string> - True if value contains given string

|(not) - Negates current value

|(matches)<regex> - True if regular expression matched on the given value (getmatch is successful)

|(lowerthan)<number> - True if current value is lower than given number

|(greaterthan)<number> - True if current value is greater than given number
 

Special parameters

There are also some specialties, that can be used in various scenarios:

|(saveas)<key> - Probably most powerful one, it saves current value to the selector (under current resolving context) which can be later used in some other macro, e.g. as result of some other operation.

|(notrecursive) - Explicitly disables the recursion of the macros, for security reasons, you can only disable it but not reenable it

|(handlesqlinhection) - You explicitly say that the resolver should handle the SQL injection of the result. Same as previous, you can only enable it for security reasons

|(resolve) - If current value contains macros, it resolves them so you can work further with the results (e.g. compare them to something)

This ends you quite rich list of new macro parameters in Kentico CMS 5.5, but that is not all, there are two more things to cover ...

Nested macros

If you worked with some more complex macros, you know that you can use something like this:

{%DocumentName|(equals)Home|(truevalue){?param?}|(user)katerinap|(hash)0da16cf6677e93c92b0374aaa529061d3ec17b7bb11c9bd7c6b0ecfcd8c409db%}

But you cannot do something like this in 5.0:

{%DocumentName|(equals)Home|(truevalue){%param%}%}

The difference is in type of macros. For performance reasons (faster processing) we didn't allow nesting of the same kind of macros so we can keep the macro regex as simple as possible.

With 5.5, there is an option to solve this. We offer using of coupled brackets to allow nesting of macros of the same type, an example explains this best:

{%DocumentName|(equals)Home|(truevalue){(1)%param%(1)}|(user)katerinap|(hash)bcd6a7a6472002d4013978782b6b3e48ff7e7b6ed8ed496bb2f0ba7dbef99ff8%}

What you do is you can put a number between the macro bracket and the type character, which makes it easier for you to see where the end of your expression is, and also possible to the engine to see it correctly. The number does not have any information value, it is just a number to detect the match. It can be any non-negative integer.

Escaping of macro parameters

Imagine situation you want to use a value containing |(you) in your macro, e.g.

{%CurrentUserName|(append)|(you)%}

For the engine, it looks like the beginning of the next parameter. With macros of 5.5, you can escape this to tell the engine this is still part of the parameter, you just need to escape the | with backslash like this:

{%CurrentUserName|(append)\|(you)%}

Now the engine sees that | is not an end of the parameter, but still a value.

You can also use \n to use new line in parameters.

Summary

There is a lot of new options in 5.5 macros, so you can go ahead and take advantage of them. If you don't need them, it doesn't matter, it is up to you as always.

What can be expected in 6.0

As I noted at the beginning, there are some major steps that 6.0 will take regarding the macro processing, it may change a little during its development, but here is some brief summary of what I expect. 

  • Support of IEnumerable in macros, treating value as a collection - In such case, the parameters will be applied to each item in the collection individually
  • Support of browsing through the object model of Kentico CMS - Imagine doing things like
{%CurrentDocument.Parent.DocumentModifiedByUser.FullName|(user)katerinap|(hash)6e682a13869dc63d8b2fd414b9b78e2b6f99ce098512206d122893ae507a1eec%}
  • Support for purely text transformations with macros - No more ASPX code unless you want to, completely safe against your designers. So the transformation cms.ecommerce.invoicesku can be like:
<tr><td>{%SKUName%}</td><td>{%SKUPrice%}</td>...</tr>
  • Support of transformations directly in macros - Imagine doing things like {%CurrentDocument.Children|(applytransformation)cms.news.summary|(user)katerinap|(hash)4813de11cae32d7bad2c9cf1b38a8343c274c2cd2455b3358d81f9c63bbd3b96%} which will render the list of items without the need for repeater. For instance, for the transformation above, you will have following macro in your invoice solving all your invoice customization issues forever:
<table>{%OrderItems|(applytransformation)cms.ecommerce.invoicesku|(user)katerinap|(hash)e6248b84231d6c4e47b14bfc073d40dad96f2299abe5820dbfb009b2ceaf8d38%}</table>
  • Support of queries in macros to further filter and specify results
{%CurrentDocument.Children|(where)NodeOrder >= 3|(orderby)NewsReleaseDate DESC|(user)katerinap|(hash)f6de28ada1f3c9eee57a232757490aebe126aaf170e36f190dc14518bd781691%}
  • Support of caching of the macro results - Say how this particular result should be cached
{#getmyvalue|(cacheminutes)10|(cachekey)MyValueOnThisPage#}

And probably some more in addition to this ...

This is all very closely connected to the better API (backwards compatible) where all objects and documents will be interconnected to a hierarchy which will also serve as LINQ to Kentico for querying data. I will elaborate some more on this later.

Now you finally see why I already almost believe you will be able to render entire page with macros in 6.0 :-), cause lot of this is already in stage of prototypes ...

See you next time!
Share this article on   LinkedIn Google+

Martin Hejtmanek

Hi, I am the CTO of Kentico and I will be constantly providing you the information about current development process and other interesting technical things you might want to know about Kentico.

Comments

ralph commented on

I agree on the documentation part. Just played around with this stuff a bit. You can do amazing stuff with it. There is however a good page here that is listing all available macros: http://bit.ly/f2Rqnh

Ryan commented on

Good info! I wish there was documentation available that would describe all available macros though...without some searching I would have never found this posting. I think it would be great to greatly expand the Macros appendix in the documentation developer guide to show a list of all available macros, what they do, and what parameters are available. Thanks!

Martin Hejtmanek commented on

Hi Jeroen,

The parameters (functions) are always applied also on custom macros as before. You still get the full macro expression so you can parse it further. What is better is that the parameters are available through property MacroResolver.CurrentParameters as two-dimensional array of strings and you can manipulate with them.

Jeroen Fürst commented on

Hi Martin,

As usual great post! I was wondering if these new macro functions work with custom macro's and if it's easy to integrate even more custom functions in 5.5?

Jeroen

Martin Hejtmanek commented on

It was sort of a natural selection of having something easily readable and not coliding with anything else in the system or HTML.

Mark Prins commented on

Thanks Martin, the changes look great and will be very useful. I can't wait for version 6.0 now - very impressive functionality planned there!

One query, there might be a typo above, should the (handlesqlinhection) item be (handlesqlinjection)?

Mark

ctaleck IPAGlobal commented on

Is seems like you developing a new macro language. What is the reasoning behind the syntax of your macro system? Is there any precedent to how it looks or did you come up with this parenthesis and pipe concept internally?


Leave message


 
Is two = five ? (true/false)