Want to help your business user? Create a custom Macro

When talking to business users (people that actually work with Sitecore on a day to day base) I created a small list of minor custom changes that would help the business users with there day to day work.

One of the items on this list is the condition for User Profile fields, by default the input of this field is a manual typed String. Which is a painful process for the business user They are not sure of the fieldname and go to the usermanager to verify the name. And even when they know the fieldname they surely can make a typo. After all even a business user is just human.

Typo and human errors

So after a small thought I started coding. My solution for this problem is simple. Create  a custom Macro with a tree selection and return the item name! Let’s do this!

Create the custom Macro class

Open VS, and create a class called UserProfileFieldMacro that inherits from the IRuleMacro class.

Override the Execute method, in this method we need to define the custom Macro. Please read the code comments for explanation.

The UserProfileFieldMacro class has the following code
[code language=”c#”]
public class UserProfileFieldMacro : IRuleMacro
{
public UserProfileFieldMacro() { }

public void Execute(XElement element, string name, UrlString parameters, string value)
{
Database coreDatabase = Sitecore.Configuration.Factory.GetDatabase(“core”);

//Define default path to profile template
string profilePath = “/sitecore/templates/System/Security/User”;

//Check if custom path is set in Macro parameters
if (parameters.HasPath)
profilePath = parameters.Path;

Item userProfileRootItem = coreDatabase.GetItem(profilePath);

//The path isn’t correct, we want to log this error and send the user to the system administrator
if (userProfileRootItem == null)
{
string error = string.Format(“The path ({0}) set in the user profile field condition doesn’t exist”, profilePath);
Sitecore.Diagnostics.Log.Error(error, this);
SheerResponse.ShowError(error + ” Please contact your system administrator”, string.Empty);
return;
}

SelectItemOptions options = new SelectItemOptions();

//Set and hide the root of the tree in the dialog window
options.Root = userProfileRootItem;
options.ShowRoot = false;

//User can only select Templatefields
options.IncludeTemplatesForSelection = new List { TemplateManager.GetTemplate(TemplateIDs.TemplateField, coreDatabase) };

//Return the item name to the public property
options.ResultType = SelectItemOptions.DialogResultType.Name;

//Set dialog window info
options.Title = “Select User profile field”;
options.Text = “Select the user profile field to use in this rule.”;
options.Icon = “applications/32×32/media_stop.png”;

//Trigger the dialog window
SheerResponse.ShowModalDialog(options.ToUrlString().ToString(), true);
}
}
[/code]
Compile your code.

Create the Items in Sitecore

Login to Sitecore and open the content editor.

Create the macro item

Go to  the /sitecore/system/Settings/Rules/Common/Macros folder and create a new Macro item called UserProfileFieldMacro. Set the Type field to match your custom Macro class and assembly.

image

Create the condition item

Go to the /sitecore/system/Settings/Rules/Common/Conditions/Security folder and duplicate the User Profile Condition Item, name the duplicated itemUser Profile Field Selection Condition.

Now we need to change the Text field so it will use the custom create Macro instead of the default string input. Go to the Text field, I highlighted the part that we are changing

where user profile [FieldName,,,specific] field [operatorid,StringOperator,,compares to] [Value,,,value]

What you see is a four column seperated string. The four columns are representing the following functionatily:

  1. the public property in the condition that will be set.
  2. the macro used to get the user input (blank is string input)
  3. optional parameters to pass to the macro
  4. the text value that appears to the business user when the value is not set

Now change this to use the custom created Macro

where user profile [FieldName,ProfileFieldOperator,,specific] field  [operatorid,StringOperator,,compares to] [Value,,,value]

If you look at the Macro code you will see that if no parameters are passed we use the path to the default user profile. If you have custom user profile you can pass the path as a parameter. For example;

where user profile [FieldName,ProfileFieldOperator,/sitecore/templates/System/Security/Jetstream User,select] field [operatorid,StringOperator,,compares to] [Value,,,value]

Now where all set to test the new condition!

Activate and test the condition

Open the page editor and select a presentation component you want to personalize. Click the personalize button and create a new rule.

Select the for the where user profile select field compares to value  condition and click the red select link. The custom Macro will now fire the dialog window where all the profile fields are available for selection.

image

And the best thing is if the business user selects a Field section the a message will appear. No more humon errors here 🙂

So for my example I configured the following rule for a component called Summer sun.

image

Test the conditional rendering rule

If I visit the website as a logged-in user with the firsts name Pieter the Summer sun component will appear. Otherwise the component will be hidden.

Let’s visit the website as a anonymous visitor.

image

Now after I login with my account (and yes my first name is Pieter) the control will appear.

image

Wrap up!

This solution is specifically created for user profile fields, you could also create a more generic Macro with more custom parameters. For me the most important part is that I showed you how easy it is to create a custom Macro. The next time the business user confront you with their problems, don’t just tell them this is the way Sitecore works. Tell them that you can easily change the behavior of Sitecore within a few hours. And of course share your customizations with us!

Don’t forget to hide the old user profile field condition. Read the article about removing conditions and actions for you business user based on Security.

If you have any questions, comments or own customizations you want to share? Please leave a comment.

Happy coding!

Design Patterns(C#): Basic example Strategy pattern

In this example I´m going to explane the strategy pattern. With this example you can do a calculation with two numbers (-/+) and expand the number of operators (/, *, etc.).

First create a interface which defines the interface of the operator classes. For this example an operator can only calculate two values.
[code language=’c#’]
//The interface for the strategies
public interface ICalculateInterface
{
//define method
int Calculate(int value1, int value2);
}
[/code]

Next create the operators (strategies) Minus (which subtract value 2 from value 1) and Plussus (which addition value 1 with value 2). The classes need to inherit from the interface ICalculateInterface.
[code language=’c#’]
//Strategy 1: Minus
class Minus : ICalculateInterface
{
public int Calculate(int value1, int value2)
{
//define logic
return value1 – value2;
}
}

//Strategy 2: Plussus
class Plussus : ICalculateInterface
{
public int Calculate(int value1, int value2)
{
//define logic
return value1 + value2;
}
}
[/code]

At last we need to create a Client that will execute the strategy.
[code language=’c#’]
//The client
class CalculateClient
{
private ICalculateInterface calculateInterface;

//Constructor: assigns strategy to interface
public CalculateClient(ICalculateInterface strategy)
{
calculateInterface = strategy;
}

//Executes the strategy
public int Calculate(int value1, int value2)
{
return calculateInterface.Calculate(value1, value2);
}
}
[/code]

Now we have two operators (minus & plussus) and a client (CalculateClient) that can execute the operators. Let’s test the code. Create a new webapplication, console app or something else that can write output. For this example I will use a webpage.

Initialize a new CalculateClient with argument operator Minus of Plussus and Calculate two values.
[code language=’c#’]
protected void Page_Load(object sender, EventArgs e)
{
CalculateClient minusClient = new CalculateClient(new Minus());
Response.Write(“
Minus: ” + minusClient.Calculate(7, 1).ToString());

CalculateClient plusClient = new CalculateClient(new Plussus());
Response.Write(“
Plussus: ” + plusClient.Calculate(7, 1).ToString());
}
[/code]

This code will give the following output.
[code language=’html’]

Minus: 6

Plussus: 8
[/code]

The great thing about this pattern is that you can easily add new opertators (strategies) to your code.

Cheers,
Pieter

Asp.Net: keyboard sort items

As proof of concept I wanted to sort images in a Grid by keyboard. The sort logic needed to be implemented on the server. My solution for this problem is a combination of Javascript and C#.

First add following html to you .aspx. Notice that the body tag has runat=”server” and a ID.
[code language=’html’]
<body runat=”server” ID=”bodyTag”>

[/code]

Now add the following JavaScript to your page. This script will fetch all keyboard input and press the corresponding button.
[code language=’js’]

document.onkeydown = checkKeycode
function checkKeycode(e) {
var keycode;
if (window.event) keycode = window.event.keyCode;
else if (e) keycode = e.which;
switch (keycode) {
case 37:
var obj = document.getElementById(”);
obj.focus();
obj.click();
break;
case 38:
var obj = document.getElementById(”);
obj.focus();
obj.click();
break;
case 39:
var obj = document.getElementById(”);
obj.focus();
obj.click();
break;
case 40:
var obj = document.getElementById(”);
obj.focus();
obj.click();
break;
}

}

[/code]

At last we need to add the following C# code to the page.
[code language=’c#’]
protected void Page_Load(object sender, EventArgs e)
{
//Ad clientside onkeypress event to the body
bodyTag.Attributes.Add(“OnKeyPress”, “keyhandlers()”);
}

protected void DownButton_Command(object sender, CommandEventArgs e)
{
//Just for testing
clickedLabel.Text = (string)e.CommandArgument;
}
[/code]

Enjoy, Pieter

C#: Get Parent Control with Generics

I use the following method to return a parent control of a specific type. This method is recursive and uses generics.

[code language=’c#’]
private Control GetParentControl(Control control)
{
if (control.Parent.GetType() == typeof(T1))
{
return control.Parent;
}
else
{
return GetParentControl(control.Parent);
}
}

[/code]

MemoryStream to Byte Array (Byte[])

With the following code you can convert your MemoryStream to a Byte Array.

[code language=’c#’]
//create new Bite Array
byte[] biteArray = new byte[memoryStream.Length];

//Set pointer to the beginning of the stream
memoryStream.Position = 0;

//Read the entire stream
memoryStream.Read(biteArray, 0, (int)memoryStream.Length);
[/code]

C#: Remove line from textfile

With the following code you can remove a line from a textfile (web.config). If the string is within a line the line will be removed.

[code language=’c#’]
string configFile = @”C:devweb.config”;
List lineList = File.ReadAllLines(configFile).ToList();
lineList = lineList.Where(x => x.IndexOf(“<!–") <= 0).ToList();
File.WriteAllLines(configFile, lineList.ToArray());

[/code]