Monthly Archives: October 2014

Make your own custom Standard Values tokens in Sitecore

StandardValuesHeader

I just love Sitecores’s Standard Values. They allows you to set default values on an item when it’s created. There are some really nice posts explaining the basics of Standard Values. Please read John West’s post – ALL ABOUT STANDARD VALUES IN THE SITECORE ASP.NET CMS and Jens Mikkelsen’s post – Standard Values in Sitecore

A very cool thing in Standard Values is the tokens. You just enter a token in the field on the standard values and then it will be replaced with an other value when the item is created. The tokens you can use are:

  • $name: The name for the new item entered by the user
  • $id: The ID of the new item
  • $parentid: The ID of the parent of the new item
  • $parentname: The name of the parent of the new item
  • $date: The system date in yyyyMMdd format
  • $time: The system time in HHmmss format
  • $now: The system date and time in yyyyMMddTHHmmss format

But would it not be nice if you could do your own custom tokens? I found some very nice posts about that subject:
John WestADD CUSTOM STANDARD VALUES TOKENS IN THE SITECORE ASP.NET CMS
Mike ReynoldsContent Manage Custom Standard Values Tokens in the Sitecore Client

While working with the PushBroker Manager I needed to get a value from an ancestor item and put it in a descendant item when it was created. In this case I wanted to know what market campaign(DMS) was selected in the campaign item
PushBrokerManagerCampaignArrow
and put it in the “Query Parameters” field in the message item.
PushBrokerManagerMessageArrow

Shorting it down – I need to get values from an ancestor item and put them in a descendant item when it is created. I want to do it by using custom tokens.

The custom tokens will be defined in Sitecore so I did a StandardValue Variable item:
StandardValueFolder

And here is the template:
StandardValueTemplate

  • StandardValueVariableToken: The token
  • StandardValueVariableAncestorOrParentFieldName: The field from the parent/ancestor item
  • StandardValueVariableAncestorOrParentFieldId: The field from the parent/ancestor item
  • StandardValueVariableAncestorOrParentItemTemplateName: The template from the parent/ancestor item
  • StandardValueVariableAncestorOrParentItemTemplateId: The template from the parent/ancestor item
  • StandardValueVariableSourceItemFieldName: The field from the source item in the parent/ancestor item selected from a Droplink, Multilist or a Treelist
  • StandardValueVariableSourceItemFieldId: The field from the source item in the parent/ancestor item selected from a Droplink, Multilist or a Treelist
  • StandardValueVariableSourceItemTemplateName: The template from the source item in the parent/ancestor item selected from a Droplink, Multilist or a Treelist
  • StandardValueVariableSourceItemTemplateId: The template from the source item in the parent/ancestor item selected from a Droplink, Multilist or a Treelist

Next thing to do will be to put the custom tokens in a repository class.

public class StandardValueVariableRepository
{
    public static StandardValueVariable Get(Item item)
    {
        if (item == null || !item.IsDerived(Constants.Templates.StandardValueVariable))
            return null;

        return StandardValueVariableFactory.Create(item);
    }


    public static StandardValueVariable Get(ID itemId, Language language)
    {
        return Get(SitecoreItemRepository.Get(itemId, language));

    }

    public static IEnumerable<StandardValueVariable> Collection(Language language)
    {
        Item standardValueVariablesFolderItem =
            SitecoreItemRepository.Get(Constants.Items.StandardValueVariablesFolder, language);

        return standardValueVariablesFolderItem.GetChildren().Select(Get);
    }

}

Here is is the factory class.

internal class StandardValueVariableFactory
{
    internal static StandardValueVariable Create(Item item)
    {
        return new StandardValueVariable()
        {
            Id = item.ID.ToString(),
            Token = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableToken),
            AncestorOrParentTemplateName = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableAncestorOrParentItemTemplateName),
            AncestorOrParentTemplateId = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableAncestorOrParentItemTemplateId),
            AncestorOrParentFieldName = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableAncestorOrParentFieldName),
            AncestorOrParentFieldId = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableAncestorOrParentFieldId),
            SourceTemplateName = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableSourceItemTemplateName),
            SourceTemplateId = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableSourceItemTemplateId),
            SourceFieldId = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableSourceItemFieldId),
            SourceFieldName = item.GetString(Constants.Fields.StandardValueVariable.StandardValueVariableSourceItemFieldName)
        };
    }
}

And the model:

public class StandardValueVariable
{
    public string Id { get; set; }
        
    public string Token { get; set; }

    public string AncestorOrParentTemplateName { get; set; }

    public string AncestorOrParentTemplateId { get; set; }

    public string AncestorOrParentFieldName { get; set; }

    public string AncestorOrParentFieldId { get; set; }

    public string SourceTemplateName { get; set; }

    public string SourceTemplateId { get; set; }

    public string SourceFieldName { get; set; }

    public string SourceFieldId { get; set; }
}

The actual “replacing” happens in the class Sitecore.Data.MasterVariablesReplacer which means I need to replace it with my own class but I don’t want to interfere with the standard tokens from Sitecore.

public class CustomTokensMasterVariablesReplacer : MasterVariablesReplacer
{

    private List<StandardValueVariable> _standardValueVariables;

    private IEnumerable<StandardValueVariable> StandardValueVariables
    {
        get
        {
            if (_standardValueVariables != null)
                return _standardValueVariables;

            _standardValueVariables = StandardValueVariableRepository.Collection(Sitecore.Context.Language).ToList();

            return _standardValueVariables;
        }
    }

    public override string Replace(string text, Item targetItem)
    {

        return GetReplacedCustomTokenValue(text, targetItem);

    }


    private string GetReplacedCustomTokenValue(string tokenText, Item targetItem)
    {
        string replacedText = base.Replace(tokenText, targetItem);

        if (replacedText != tokenText)
            return replacedText;

        foreach (StandardValueVariable standardValueVariable in StandardValueVariables)
        {
            if (standardValueVariable == null)
                continue;

            if (string.IsNullOrWhiteSpace(standardValueVariable.Token))
                continue;

            if (!tokenText.Contains(standardValueVariable.Token))
                continue;

            if (string.IsNullOrWhiteSpace(standardValueVariable.AncestorOrParentTemplateId) && string.IsNullOrWhiteSpace(standardValueVariable.AncestorOrParentTemplateName))
                continue;

            if (string.IsNullOrWhiteSpace(standardValueVariable.AncestorOrParentFieldName) && string.IsNullOrWhiteSpace(standardValueVariable.AncestorOrParentFieldId))
                continue;

            if (string.IsNullOrWhiteSpace(standardValueVariable.AncestorOrParentTemplateId))
                continue;

            Item ancestorItem = targetItem.GetAncestors()
                .FirstOrDefault(item => item.IsDerived(new ID(standardValueVariable.AncestorOrParentTemplateId)));

            if (ancestorItem == null)
                continue;

            if (string.IsNullOrWhiteSpace(standardValueVariable.SourceTemplateId) &&
                string.IsNullOrWhiteSpace(standardValueVariable.SourceFieldId))
            {
                return ancestorItem.GetString(new ID(standardValueVariable.AncestorOrParentFieldId));
            }

            //This is for treelist, droplink or multilist
            Item sourceItem = ancestorItem.GetItemValues(new ID(standardValueVariable.AncestorOrParentFieldId)).FirstOrDefault();

            if (sourceItem == null)
                continue;

            //Checking if it's a campaign(DMS). I was going crazy trying to get the value from an iframe field. 
            //Instead I just took the the campaign id... 
            if (sourceItem.IsDerived(Constants.Templates.AnalyticsCampaign) && standardValueVariable.SourceFieldId == Constants.Fields.AnalyticsCampaign.CampaignLink.ToString())
                return string.Format("sc_camp={0}", sourceItem.ID.ToShortID());

            if (!string.IsNullOrWhiteSpace(standardValueVariable.SourceFieldId))
                return sourceItem.GetString(new ID(standardValueVariable.SourceFieldId));
        }


        return string.Empty;

    }

}

I inherit the Sitecore.Data.MasterVariablesReplacer and override method Replace(This is where the replacing of tokens is done).
In GetReplacedCustomTokenValue I use the base.Replace from the inherited class to get the “replaced value”. If the “replaced value” is the same as the token text it means that “the replacing” was not successful. In other words – here is a custom token.

Next thing will be to iterate through the custom tokens(StandardValue Variable items), find the correct one and from it’s settings together with targetItem return the “replaced value”. That’s it πŸ™‚

Oh I almost forgot, here is the patch config file.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <settings>
      <setting name="MasterVariablesReplacer">
        <patch:attribute name="value">Sitecore.Sharedsource.CustomTokensMasterVariablesReplacer,Sitecore.Sharedsource</patch:attribute>
      </setting>
    </settings>
  </sitecore>
</configuration>

That’s all for now folks πŸ™‚

Advertisements

Send Safari Push Notifications to your Mac users using Sitecore – part 3

PushBrokerManager

Push notifications have been around for a while and we have all experienced them in our phones and tablets. Sending notifications to web users have also been possible but the browser and the website must be active.

But did you know that you can send push notifications to your web users even when the browser is closed? It’s called Safari Push Notifications and was presented when Apple released their latest OS – Maverick. It works on Safari running on Maverick – basically every Mac user.

In my previous post, Send Safari Push Notifications to your Mac users using Sitecore – part 1, I showed you guys how to prepare your website for Safari Push Notifications and store the the receivers/visitors in Sitecore.
In this post I will show you to push/send notifications by using the best CMS platform ever – Sitecore πŸ™‚

In order to send push notifications we need the device token(receiver id – which was described in previous post) and a connection to APNS(Apple push notification server). There are a lot of push notification services who can help you with that, like Notification Hubs, Urban Airship, ZeroPush etc. But I wanted to use something that is more integrated to Sitecore and I found a very nice open source library PushSharp – A server-side library for sending Push Notifications to iOS (iPhone/iPad APNS), OSX (APNS 10.8+) Android (C2DM and GCM – Google Cloud Message), Chrome (GCM) Windows Phone, Windows 8, Blackberry (PAP), and Amazon (ADM) devices

So this what I did: I took PushSharp, integrated it with Sitecore and did a module of it – PushBroker Manager. The main idea is to make the PushBroker Manager work like the Email Experience(Campaign) Manager. Instead of sending emails it will send push notifications. It’s a long way to go so I will start with the basics like sending notifications, be able to hook up campaigns, do follow ups, do geofence notifications.

In order to send a push notification(using the PushBroker Manager) you need to create a campaign and map a market campaign to it(optional):
PushBrokerManagerCampaign

Next thing is to create the actual message(push notification)
PushBrokerManagerMessage

Add/Select the subscribers(receivers)
StoreReceiver

A scheduled task will send the notification to the subcsribers and move the notification message to the “Proccessed Messages” folder
ProccessedMessage

This is how the notification will be presented for the subscriber
AlertView
(Click on the “View” button will direct the user to the website and trigger a campaign)

Let’s show it in code instead πŸ™‚
I will just take the part when the message will be send to the subscribers/receivers

public void SendWebMessages(IList<string> receiversIds, IMessage pushMessage, Campaign campaign)
{
            
    foreach (string receiver in receiversIds)
    {

        PushBrokerNotification pushBrokerNotification = new PushBrokerNotification()
            {
                CampaignId = campaign.Id,
                DeviceTokenId = receiver,
                PushMessageId = pushMessage.Id,
                LanguageCode =  languageCode
            };

        try
        {

              
            Push.QueueNotification(new AppleNotification()
                                .ForDeviceToken(receiver)
                                .WithPayload(new AppleSafariNotificationPayload(pushMessage.PushMessage.Name, pushMessage.PushMessage.Text, pushMessage.PushMessage.ButtonLabel, pushMessage.PushMessage.QueryParameters))
                                .WithTag(pushBrokerNotification)
                                );

              

        }
        catch (Exception ex)
        {
            Sitecore.Diagnostics.Log.Error(String.Concat("Error when trying to send to receiver:", receiver, ", pushmessage: ", pushMessage.Id, ", Campaign: ", campaign.Id), this);
        }
    }
}

There are some interesting events you can hook up on, for instance the PushOnOnNotificationSent. In this case if the message is successfully send I’ll set a “message send” date on the subscriber/receiver.

private void PushOnOnNotificationSent(object sender, INotification notification)
{
    PushBrokerNotification message = notification.Tag as PushBrokerNotification;

    if (message == null)
        return;

    Sitecore.Diagnostics.Log.Info(string.Format(CultureInfo.InvariantCulture,
                        "Notification {0} for device {1} has been sent successfully",
                        message.PushMessageId, message.DeviceTokenId), this);

    NotificationService notificationService = new NotificationService();
    notificationService.NotificationMessageSent(message.DeviceTokenId, message.PushMessageId, message.LanguageCode);
}

I will do a post on how the PushBroker Manager will work for the IOS apps πŸ™‚
Hopefully the PushBroker Manager will be released soon on the Sitecore Market Place.

That’s all for now folks πŸ™‚

Send Safari Push Notifications to your Mac users using Sitecore – part 2

SafarIPush

Push notifications have been around for a while and we have all experienced them in our phones and tablets. Sending notifications to web users have also been possible but the browser and the website must be active.

But did you know that you can send push notifications to your web users even when the browser is closed? It’s called Safari Push Notifications and was presented when Apple released their latest OS – Maverick. It works on Safari running on Maverick – basically every Mac user.

In my previous post, Send Safari Push Notifications to your Mac users using Sitecore – part 1, I showed you guys how to prepare your website for Safari Push Notifications and store the the receivers/visitors in Sitecore.
In this post I will show you the benefits of using Safari Push Notifications, not only to push/send notifications but also to identify visitors

I recently read John West(Sitecore) Blog – Share Your Common Sense. So I will try to keep my blog post short πŸ™‚

When the receivers(visitors) accept push notifications from the website(see my previous post) they will automatically be registered into Sitecore.
PushNotifications
Accepting push notifications

StoreReceiver
Receivers in Sitecore

When the visitors accept push notifications they will receive a device ID which is generated by Apple. The ID is unique for that computer and stays permanently. That means you don’t have to store it in a cookie. Next time the visitor enters the website we can easily check the device ID by using following JavaScript code:

var PushBroker = PushBroker || {};


jQuery(document).ready(function () {
    PushBroker.SafariNotifications.DomReady();
});


PushBroker.SafariNotifications = {
    DomReady: function() {

        var safariNotifications = new PushBroker.SafariNotifications.Init();

        safariNotifications.prepareForPushNotification();

    },
    Init: function() {

        var self = this;

        self.dataContainer = jQuery(".notificationSettings");

        self.prepareForPushNotification = function () {
            "use strict";

            if ('safari' in window && 'pushNotification' in window.safari) {
                var permissionData = window.safari.pushNotification.permission(self.dataContainer.data("pushId"));
                checkRemotePermission(permissionData);
            } else {
                alert("Push notifications not supported.");
            }
        };

        self.checkRemotePermission = function (permissionData) {
            "use strict";

            if (permissionData.permission === 'default') {
                console.log("The user is making a decision");
                window.safari.pushNotification.requestPermission(
                    self.dataContainer.data("restURL"),
                    self.dataContainer.data("pushId"),
                    {},
                    checkRemotePermission
                );
            } else if (permissionData.permission === 'denied') {
                console.dir(arguments);
           } else if (permissionData.permission === 'granted') {
                console.log("The user said yes, with token: " + permissionData.deviceToken);
           }
        };

    }
}

The last part permissionData.permission === ‘granted’ allows us to get the ID permissionData.deviceToken. Yes! We now have a unique ID for that visitor on that computer which stays permanently and we can hook it up in the DMS – So if you don’t want to send push notifications, use it to identify visitors πŸ™‚

That’s all for now folks πŸ™‚