API Questions on Kentico API.
Version 6.x > API > Registering Custom Macros View modes: 
User avatar
Kentico Legend
Kentico Legend
Brenden Kehren - 11/22/2011 9:32:54 PM
   
Registering Custom Macros
I've followed the documentation here http://devnet.kentico.com/docs/6_0/devguide/index.html?registering_cutom_macro_methods.htm and can't seem to get my simple macro to register.

Here is my code, what did I miss?


public static class MCACustomMacroClass
{
/// <summary>
/// Registers all blog methods to macro resolver.
/// </summary>
public static void RegisterMethods()
{
MacroMethods.RegisterMethod("GetFiscalYear", GetFiscalYear, typeof(string), "Returns fiscal year in yyyy string format.", null, 1, new object[,] { { "CurrentDate", typeof(DateTime), "Current date to get fiscal year from." } }, null);
}


#region "Macro methods implementation"

/// <summary>
/// Gets the fiscal year based on the date passed in.
/// </summary>
/// <param name="CurrentDate"></param>
/// <returns>The fiscal year yyyy in a string format</returns>
public static string GetFiscalYear(DateTime CurrentDate)
{
String ReturnValue = string.Empty;
CurrentDate = CurrentDate.AddMonths(9);
ReturnValue = CurrentDate.Year.ToString();
return ReturnValue;
}

#endregion


#region "MacroResolver wrapper methods"

public static object GetFiscalYear(params object[] parameters)
{
switch (parameters.Length)
{
case 1:
return GetFiscalYear(ValidationHelper.GetDateTime(parameters[0], DateTime.Now));
default:
throw new NotSupportedException();
}
}

#endregion
}



public partial class CMSModuleLoader
{
private class MCAMacroLoaderAttribute : CMSLoaderAttribute
{
/// <summary>
/// Registers module methods.
/// </summary>
public override void Init()
{
// -- Custom macro methods
MCACustomMacroClass.RegisterMethods();

}
}
}

User avatar
Kentico Developer
Kentico Developer
kentico_ivanat - 11/24/2011 6:23:00 AM
   
RE:Registering Custom Macros
Hi,

how did you call your method?

Could you please try to use this:

{% Now.Date.GetFiscalYear(Now) %}

Then I changed registration of your method and added comment to its implementation. Please copy/paste these changes. Then you should see the method correctly:


MacroMethods.RegisterMethod("GetFiscalYear", GetFiscalYear, typeof(string), "Returns fiscal year in yyyy string format.", null, 1, new object[,] { { "CurrentDate", typeof(DateTime), "Current date to get fiscal year from." } }, null);


/// <summary>
/// Gets the fiscal year based on the date passed in.
/// </summary>
/// <param name="CurrentDate">CurrentDate</param>
/// <returns>The fiscal year yyyy in a string format</returns>
public static string GetFiscalYear(DateTime CurrentDate)
{
String ReturnValue = string.Empty;
CurrentDate = CurrentDate.AddMonths(9);
ReturnValue = CurrentDate.Year.ToString();
return ReturnValue;
}


Best regards,
Ivana Tomanickova

User avatar
Kentico Legend
Kentico Legend
Brenden Kehren - 11/28/2011 10:52:47 PM
   
RE:Registering Custom Macros
Finally got a chance to try your suggestions and still no success. In fact, your code is no different from what I already have except your usage in the macro.

How does Kentico determine where a custom macro will be placed? In your example you say to use Now.Date.<macroname>. How do I know it will be nested within Now.Date?

User avatar
Kentico Developer
Kentico Developer
kentico_ivanat - 11/29/2011 3:10:09 AM
   
RE:Registering Custom Macros
Hi,

I included the whole CustomMacroMethod.cs file so you can compare it with yours file. In the registration SampleMacroModule I have simply commented out following code in the Init() method:
CustomMacroMethods.RegisterMethods();

How does Kentico determine where a custom macro will be placed?
According to the type of the first attribute in the registration of method. In your case the first parameter is DateTime so the method should be available for DateTime properties.

Example:
Now.Date.{your_method}

Why it behaves this way?
For example if you would implement some string method, you can call it using:
MyStringMethod("text")
or
"text".MyStringMethod()


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using CMS.GlobalHelper;

/// <summary>
/// Example of custom module with custom macro methods registration.
/// </summary>
public static class CustomMacroMethods
{
/// <summary>
/// Registers all blog methods to macro resolver.
/// </summary>
public static void RegisterMethods()
{
MacroMethods.RegisterMethod("MyMethod", MyMethod, typeof(string), "Returns concatenation of two strings.", null, 1, new object[,] { { "param1", typeof(string), "First string to concatenate." }, { "param2", typeof(string), "Second string to concatenate." } }, null);
MacroMethods.RegisterMethod("MyMethodSnippet", MyMethod, typeof(string), "Calls MyMethod on lower case current user name.", null, 0, null, null, new List<Type>() { typeof(string) }, "MyMethod(ToLower(CurrentDocument.DocumentName), |);", false);
MacroMethods.RegisterMethod("GetFiscalYear", GetFiscalYear, typeof(string), "Returns fiscal year in yyyy string format.", null, 1, new object[,] { { "CurrentDate", typeof(DateTime), "Current date to get fiscal year from." } }, null);
// Call MacroMethods.RegisterMethod for your own custom methods here with following parameters:
// 1. parameter: Method name
// 2. parameter: Method delegate (wrapper method)
// 3. parameter: Return type of the method
// 4. parameter: Comment for the method
// 5. parameter: Formatting string for "human readable" translation of the method call (optional, you do not have to specify this)
// 6. parameter: Minimal number of parameters needed to call the method (mimimal overload)
// 7. parameter: Parameter definition in format {{name, type, comment}, {name, type, comment}}
// 8. parameter: A list of special parameters needed to be supplied by resolver (these parameters are automatically passed by MacroResolver as the first parameters to the wrapper method)
// 9. parameter: List of types for which the method is applicable (set to null for all types to be allowed)
// 10. parameter: Code snippet which is used in AutoCompletion when TAB is pressed (for determining the cursor position use pipe)
}


#region "Macro methods implementation"

/// <summary>
/// Concatenates the given string with " default" string.
/// </summary>
/// <param name="param1">String to be concatenated with " default"</param>
public static string MyMethod(string param1)
{
return MyMethod(param1, "default");
}


/// <summary>
/// Concatenates two strings.
/// </summary>
/// <param name="param1">First string to concatenate</param>
/// <param name="param2">Second string to concatenate</param>
public static string MyMethod(string param1, string param2)
{
return param1 + " " + param2;
}

// Add your own custom methods here

/// <summary>
/// Gets the fiscal year based on the date passed in.
/// </summary>
/// <param name="CurrentDate">CurrentDate</param>
/// <returns>The fiscal year yyyy in a string format</returns>
public static string GetFiscalYear(DateTime CurrentDate)
{
String ReturnValue = string.Empty;
CurrentDate = CurrentDate.AddMonths(9);
ReturnValue = CurrentDate.Year.ToString();
return ReturnValue;
}
/// <summary>
/// Wrapper method of MyMethod suitable for MacroResolver.
/// </summary>
/// <param name="parameters">Parameters of the method</param>
public static object MyMethod(params object[] parameters)
{
switch (parameters.Length)
{
case 1:
// Overload with one parameter
return MyMethod(ValidationHelper.GetString(parameters[0], ""));

case 2:
// Overload with two parameters
return MyMethod(ValidationHelper.GetString(parameters[0], ""), ValidationHelper.GetString(parameters[1], ""));

default:
// No other overload is supported
throw new NotSupportedException();
}
}


public static object GetFiscalYear(params object[] parameters)
{
switch (parameters.Length)
{
case 1:
return GetFiscalYear(ValidationHelper.GetDateTime(parameters[0], DateTime.Now));
default:
throw new NotSupportedException();
}
}



// Add wrappers for MacroResolver for your own custom methods here
// The signature of wrapper methods has to be "public static object MyMethodName(params object[] parameters)"

#endregion
}


Best regards,
Ivana Tomanickova

User avatar
Kentico Legend
Kentico Legend
Brenden Kehren - 11/29/2011 9:45:23 AM
   
RE:Registering Custom Macros
Again, it appears my code is exactly the same as what you have except I have the following structure in my App_Code directory and have placed my code in different .cs files.

App_Code>CMSModules>MyFolder>Classes>MyClass.cs
App_Code>CMSModules>MyFolder>Modules>MyModule.cs

User avatar
Kentico Legend
Kentico Legend
Brenden Kehren - 11/29/2011 11:38:48 PM
   
RE:Registering Custom Macros
Well after moving my folder structure up a level to App_Code>MyFolder things started working right. Although when I tried to use it like you had mentioned, it throws a MacroResolver error stating: Error while evaluating expression: Now.Date.GetFiscalYear(CurrentDate)|(user)username|(hash)e13b944ad2950fc8bd1dfc28f733a8e703e0cb81f906323ce5e35622b270a643
[MacroExpression.Evaluate]: Method GetFiscalYear has invalid number of arguments.

But when I use it like so: GetFiscalYear(CurrentDate), it works just fine but it does not show up in the Intellisense list using the Macro builder but it does if I use it the way you suggest.

User avatar
Member
Member
cgilbu - 11/30/2011 4:41:18 AM
   
RE:Registering Custom Macros
I think it's funny that Kentico didn't, in a more obvious way, warn people about custom macros when upgrading to Kentico 6. We have almost 30 custom macros used all over our site, and none of them are working after the upgrade.

Now we have to move all of them over to the new system, and I got the same problem as you described..

User avatar
Kentico Developer
Kentico Developer
kentico_ivanat - 11/30/2011 6:25:56 AM
   
RE:Registering Custom Macros
Hi,

your old custom macros should still work. What was changed is the Init() method. It should look like:



using CMS.Compatibility;
...
public static void Init()
{
MacroResolverCompatibility.OnResolveCustomMacro += new MacroResolverCompatibility.MacroHandler(ResolveCustomMacro);
ClassHelperCompatibility.OnGetCustomClass += new ClassHelperCompatibility.GetClassEventHandler(GetCustomClass);

}


Best regards,
Ivana Tomanickova

User avatar
Member
Member
nicolas.juteau-nexun - 1/3/2012 12:08:56 PM
   
RE:Registering Custom Macros
Hi Ivana,

Unfortunately, no the old custom macros doesn't work...

I have checked the file ~/App_Code/Global/CMS/CMSCustom.cs

and the Init() method is exactly the same as the one you described in your post.

If I go through debugging and set a breakpoint in the Init() method, the breakpoint is never hit.

I even tried to call CMSCustom.Init() in the overriden Init() in SampleMacroLoaderAttribute (/App_Code/Samples/Modules/SampleMacroModule.cs and still the old custom macros are not resolved.

I tried adding the key CMS55Compatibility in web.config (still no documentation about what this key is actually doing ?), not working as well..

Is it a bug or we missed something ?

We were using 5.5 R2 and upgraded to 6.0.4371

User avatar
Kentico Developer
Kentico Developer
kentico_ivanat - 11/30/2011 6:15:14 AM
   
RE:Registering Custom Macros
Hi,

unfortunately, I could not reproduce the error message you mentioned. If you need to display the method everywhere you can change its registration and implementation to:


MacroMethods.RegisterMethod("GetFiscalYear", GetFiscalYear, typeof(string), "Returns fiscal year in yyyy string format.", null, 1, new object[,] { { "CurrentDate", typeof(object), "Current date to get fiscal year from." } }, null, null);

/// <summary>
/// Gets the fiscal year based on the date passed in.
/// </summary>
/// <param name="CurrentDate">CurrentDate</param>
/// <returns>The fiscal year yyyy in a string format</returns>
public static string GetFiscalYear(object CurrentDate)
{
DateTime currentDate = ValidationHelper.GetDateTime(CurrentDate, DateTime.Now);
currentDate = currentDate.AddMonths(9);

return currentDate.Year.ToString();
}



Then you will see the method in both cases in your transformation:

{% GetFiscalYear(Eval("NewsReleaseDate")) %}

{% Eval("NewsReleaseDate").GetFiscalYear()%}


Best regards,
Ivana Tomanickova

User avatar
Kentico Legend
Kentico Legend
Brenden Kehren - 11/30/2011 9:31:54 AM
   
RE:Registering Custom Macros
Ivana, the error I received was in the Site Manager event log not in the site's CMSDesk event log.

Also is there a specific directory structure one should use for this custom macro setup? Was I wrong in putting it in the CMSModules directory? I placed it there as I had other code for some modules that were created.

User avatar
Kentico Developer
Kentico Developer
kentico_ivanat - 12/12/2011 1:30:32 AM
   
RE:Registering Custom Macros
Hi,

the custom method code can be inserted anywhere you need. But the method has to be registered before it is used.

A lot of useful information about creating custom modules you can find in Martin's blog post.

In case of custom macro. You need to register method in Init() method which is implemented in the class which inherit from the CMSLoaderAttribute class.

Example:
  
private class SampleMacroLoaderAttribute : CMSLoaderAttribute
{
/// <summary>
/// Registers module methods.
/// </summary>
public override void Init()
{
MyClassWithMethod.RegisterEvents();
}
}


While implementing custom methods you can follow instructions described in the Devguide:
Registering custom macro methods

Best regards,
Ivana Tomanickova