Controlling Sitecore’s isLanguageFallbackValid cache size

Hi everybody,

I ended up doing some digging today and thought I’d share my findings.  I’m working with a client who’s 8.2 Sitecore instance is filling up the isLanguageFallbackValid cache and then some.  This cache size appears to be controlled via Sitecore’s “Caching.SmallCacheSize” setting.  I thought I’d share as a quick Google search didn’t immediately reveal anything.

While we’re on the subject of caching and sizing I should probably mention the “Caching.DisableCacheSizeLimits” setting.  Disabling the cache size limit can be a great way to see what your Sitecore instance is actually using so you can plan accordingly (via /sitecore/admin/cache.aspx).  Whether or not you can leave this set on is another matter.  This article by Uli Weltersbach is worth a read: https://reasoncodeexample.com/2013/03/20/sitecore-cache-settings-for-slackers/

-Dan

Advertisements

Sitecore Commerce Connect: Product Pricing

In this post I’ll be demonstrating how to retrieve product pricing using Sitecore Commerce Connect.

As with other Commerce Connect features the details of determining pricing is left up to the implementer. The preferred way to determine product pricing is by patching a processor into the appropriate pipeline. This processor will then take request inputs and either query a 3rd party commerce system or perform some other type of lookup. From here, you’ll then be able to invoke this pipeline by creating a request and calling the a method on the PricingServiceProvider. By default, commerce connect ships with two pricing related pipelines: “commerce.prices.getProductPrices” and “commerce.prices.getProductBulkPrices”.

To begin let’s add some code to a page to request the price of a product with an ID of “myProductID”.

            var pricingServiceProvider = new PricingServiceProvider();
            var request = new GetProductPricesRequest("myProductID");
            var result = pricingServiceProvider.GetProductPrices(request);
            if (!result.Success)
            {
                Log.Error("Nooooo! " + result.SystemMessages, this);
                return;
            }

            var price = result.Prices["Basic Price"].Amount;

If you’ve worked with Commerce Connect at all this should look fairly familiar to you and is a pattern you’ll use time and again while working with Commerce Connect.

First, we’re creating a new instance of a PricingServiceProvider. These providers wrap the pipeline calls and generally make the handling of inputting the request and retrieving the response easier. Once we have a provider we’ll create a request and use this to call a method that will kick off the pipeline. The request is provided to each pipeline processor in turn. For now, we’ll just be providing a product ID but Commerce Connect’s developer guide lists a number of optional parameters that can be used, but you already read the manuals, right?

Once we have the above code added we could run it but without adding our own processor nothing would happen. To add our own processor we’ll create a new configuration file.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <commerce.prices.getProductPrices>
        <processor type="LaunchSitecore.DemoStuff.Pricing, LaunchSitecore" />
      </commerce.prices.getProductPrices>
    </pipelines>
  </sitecore>
</configuration>

And of course we’ll need to create our pricing class. For processors that are part of commerce connect pipelines inheriting from the abstract class PipelineProcessor is generally recommended.

using Sitecore.Commerce.Pipelines;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace LaunchSitecore.DemoStuff
{
    public class Pricing : PipelineProcessor<ServicePipelineArgs>
    {
        public override void Process(ServicePipelineArgs args)
        {
            throw new NotImplementedException();
        }
    }
}

So far we’re calling the “getProductPrices” pipeline using the PricingServiceProvider and have created a simple processor which has been patched into this pipeline. We’ll now want to take a look at the args passed into our pipeline to determine the context of the pricing request. ServicePipelineArgs are broken down into two main parts, the Request provided when the pipeline was started and a result. It’s important to remember while implementing your processor that other processors may have run before yours and added their own results.

We’ll update our pricing class to validate the request and result.

  public override void Process(ServicePipelineArgs args)
        {
            var request = args.Request as GetProductPricesRequest;
            var results = args.Result as GetProductPricesResult;

            if (request == null || results == null)
            {
                Log.Error("LaunchSitecore.DemoStuff.Pricing unexpected requests or results", this);
            }
        }

Once we have our GetProductPricesRequest we can use the parameters provided there to look up a products pricing. We’ll finish our test implementation by simply hard code the price. If the product ID matches our ID the basic price is 9.99 otherwise its one million dollars.

public override void Process(ServicePipelineArgs args)
        {
            var request = args.Request as GetProductPricesRequest;
            var results = args.Result as GetProductPricesResult;

            if (request == null || results == null)
            {
                Log.Error("LaunchSitecore.DemoStuff.Pricing unexpected requests or results", this);
            }

            Price price;
            if(request.ProductId == "myProductID")
            {
                price = new Price { Amount = 9.99m };
            }
            else
            {
                price = new Price { Amount = 1000000.00m };
            }

            if (results.Prices.ContainsKey("Basic Price"))
            {
                //If the basic price was already set we're overriding it.
                results.Prices.Remove("Basic Price");
            }

            results.Prices.Add("Basic Price", price);
        } 

I’d like to point out a few details in the code above.

First, we haven’t’ specified what currency our price is in. In a real world scenario you’d probably end up defining the currency in the request and using that to tailor the results. I’d imagine this is probably something you don’t want to forget while creating a multinational solution.

The second item of interest is that the results contain a list of prices. We’re simply adding one price to this list by key, then using this key to output the price once the pipeline completes.

Third, we’re specifically checking the results to see if a basic price has already been defined.  There’s a chance that another processor is part of this pipeline and has already been executed.

At this point you should be able to request and determine the price for any product under a number of different conditions based on the parameters of the request. When implementing pricing in the real world there’s a chance that what’s provided out of the box doesn’t fulfill your requirements. In that case there’s really only one thing to do, panic! extend the base functionality.

This is fairly straightforward and to demonstrate this I’m going to be modifying our example so that pricing for returning customers is $1.00 less than for new customers on our test product. Just a quick note, this is for demonstrating modifying the request, there are far better ways to handle this requierment in the real world (TBH, I just couldn’t think of a better example :/).

I’ll start by creating a new class. This class will extend the GetProductPriceRequest and add a Boolean to indicate if the current customer is new or returning. I’ll add a constructor to require this Boolean to be set while instantiating the class.

public class MyPricingRequest : GetProductPricesRequest
        {
            public bool ReturningCustomer { get; protected set; }

            public MyPricingRequest(bool returningCustomer, string productId, params string[] priceTypeIds)
                : base(productId, priceTypeIds)
            {
                ReturningCustomer = returningCustomer;
            }
        }

We’ll then update our processor as well as our call to the pricing service provider to account for this.

public override void Process(ServicePipelineArgs args)
        {
            var request = args.Request as MyPricingRequest;
            var results = args.Result as GetProductPricesResult;

            if (request == null || results == null)
            {
                Log.Error("LaunchSitecore.DemoStuff.Pricing unexpected requests or results", this);
            }

            Price price;
            if (request.ProductId == "myProductID")
            {
                if (request.ReturningCustomer)
                {
                    price = new Price { Amount = 8.99m };
                }
                else
                {
                    price = new Price { Amount = 9.99m };
                }
            }
            else
            {
                price = new Price { Amount = 1000000.00m };
            }

            if (results.Prices.ContainsKey("Basic Price"))
            {
                //If the basic price was already set we're overriding it.
                results.Prices.Remove("Basic Price");
            }

            results.Prices.Add("Basic Price", price);
        }

and

private void GetPrice()
        {
            …
            var request = new LaunchSitecore.DemoStuff.Pricing.MyPricingRequest(true, "myProductID");
            var result = pricingServiceProvider.GetProductPrices(request);
            …
        }

Well there you have it. You should now be able to make the request price for a product, provide details on the context of the price needed, have the ability to inject your own code into the pricing pipelines and have idea on how to start extending the core functionality to suit your needs.

//Thanks!

Managing a shopping cart using Sitecore Commerce Connect

When developing an ecommerce solution, the ability to create and manage a shopping cart is a fairly important feature.  In this post I’m hoping to shed some light on how the shopping cart functions within Commerce Connect.  I’ll discuss what it is, how it’s managed, what it does and doesn’t do out of the box and how to extend the functionality or integrate it with another system.

First of all, the cart defined by Commerce Connect is more than just a way to save products a customer wishes to purchase.  In fact, a better way to think of the cart is more of a pending order.  In addition to products for purchase, the cart can contain shipping and payment info, addresses, discounts, etc…  As your users are providing information and making selections, such as what shipping method they prefer, it’s important to remember that these operations will all be cart centric.

From a high level the cart is managed by using Commerce Connect’s CartServiceProvider.  Different cart features can be accessed by creating a request, passing the request to the appropriate method and checking the results returned.

For Example:

            var userId = Sitecore.Analytics.Tracker.Current.Contact.ContactId;
            var cartServiceProvider = new CartServiceProvider();
            var createCartRequest = new CreateOrResumeCartRequest("website", userId.ToString());
            var results = cartServiceProvider.CreateOrResumeCart(createCartRequest);

Behind the scenes the CartServiceProvider will end up invoking one or more pipelines.  For cart related tasks you can find these pipelines within App_Config\Include\Sitecore.Commerce.Carts.config.

There are a couple of interesting points to make about the out of the box Carts.config.  First, just about every pipeline has a processor with a parameter referencing the “eaStateCartRepository”.  This is an implementation of a shopping cart that leverages Sitecore’s engagement analytics and MongoDB to store the state of a user’s shopping cart.  It comes out of the box but can be disabled if not needed.

For more information I highly recommend this video: https://www.youtube.com/watch?v=ef8M5DZXFnM

This does highlight one of the key concepts and strengths of Sitecore’s Commerce Connect in that it abstracts the implementation of the carts functionality.  If I’m developing a feature for a site I can manage the cart as needed without having to worry about if the cart is stored in the AutomationStates collection or there is a synchronization process to another system.  Further I can begin development of a project using the eaState repository and later very easily swap out how and where cart information is stored.

The other thing you’ll notice is that the pipelines eventually end up running the save cart pipeline.  This is important to keep in mind as you’re adding your custom processors.  If, for example, you’re modifying how shipping information is added to the cart you may insert a processor into the “commerce.cats.addShippingInfo” pipeline that could handle a “AddShippingInfo” request and would verify the shipping info and update the cart.  You could then create a processor in the “commerce.carts.saveCart” that would be responsible for clearing relevant caches whenever a cart was updated.

In our example above both pipelines were invoked when shipping info was associated with a cart.  Either pipeline could be used to integrate with 3rd party ecommerce systems that need access to details of a user’s cart.  Which pipeline to use will probably be dictated by the specifics of your 3rd party commerce system.  If you’re able to make a single call to update all of the details of your cart it would make sense to use the save cart pipeline.  If, however, you need to update each component of the cart individually (shipping info, payment info, line items etc…) you may want to do these in their specific pipeline to avoid unnecessary calls to the ecommerce system.

Well that’s it.  I hope this post was helpful and gave you enough background knowledge for you to sink your teeth in deeper while developing a Sitecore Commerce Connect solution.

Creating a custom index update strategy in Sitecore

Index update strategies provide a way for you to customize how and when a Sitecore index get updated. In a recent project we had an index that contained computed fields based on related items. We needed a way to update the index entry for one item when a related item was published.

As with many other things Sitecore related it all begins with a configuration change. If you take a look at the definition of an index in Sitecore you can see that it contains a strategies node. Any strategies used by the index will be listed here. When making a custom strategy you can either define it here or add it to contentSearch/indexConfigurations/indexUpdateStrategies and then reference it. For the purpose of this demo we’ll choose the former simply to keep our demo code more compact.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <databases>
      <database id="web">
        <Engines.HistoryEngine.Storage>
          <obj type="Sitecore.Data.SqlServer.SqlServerHistoryStorage, Sitecore.Kernel">
            <param connectionStringName="$(id)"/>
            <EntryLifeTime>30.00:00:00</EntryLifeTime>
          </obj>
        </Engines.HistoryEngine.Storage>
      </database>
    </databases>
    <contentSearch>
      <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
        <indexes hint="list:AddIndex">
          <index id="sitesearch_web" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider">
            <param desc="name">$(id)</param>
            <param desc="folder">$(id)</param>
            <!--
 This initializes index property store. Id has to be set to the index id
-->
            <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)"/>
            <configuration ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration"/>
            <strategies hint="list:AddStrategy">
              <!--
 NOTE: order of these is controls the execution order
-->
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/onPublishEndAsync"/>
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/rebuildAfterFullPublish"/>
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/remoteRebuild"/>

              <!-- This is our new strategy-->
              <MyUpdateStrategy type="LaunchSitecore.IndexStrategy.MyUpdateStrategy, LaunchSitecore">
                <Database>web</Database>
              </MyUpdateStrategy>
            </strategies>
            <commitPolicyExecutor type="Sitecore.ContentSearch.CommitPolicyExecutor, Sitecore.ContentSearch">
              <policies hint="list:AddCommitPolicy">
                <policy type="Sitecore.ContentSearch.TimeIntervalCommitPolicy, Sitecore.ContentSearch"/>
              </policies>
            </commitPolicyExecutor>
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.ContentSearch.SitecoreItemCrawler, Sitecore.ContentSearch">
                <Database>web</Database>
                <Root>/sitecore/content/Home</Root>
              </crawler>
            </locations>
          </index>
        </indexes>
      </configuration>
    </contentSearch>
  </sitecore>
</configuration>

As you can see we’re creating a new index for this demo. This index is a copy of the Sitecore_web_index but adds a MyUpdateStrategy node to the strategies. MyUpdateStrategy consists of two main parts. The type attribute and a database. The type attribute references a class that defines our implementation of this strategy while the database value will be passed into our class and provides an easy way to manage settings from the configuration file.

Next we’ll create our MyUpdateStrategy class. This class will need to implement IIndexUpdateStrategy but should also have a public property named “Database”. This property will be set from the configuration values.

So far we should have the following. Let’s take a break and test this out.

namespace LaunchSitecore.IndexStrategy
{
    public class MyUpdateStrategy : IIndexUpdateStrategy
    {
        public string Database { get; set; }

        public void Initialize(Sitecore.ContentSearch.ISearchIndex searchIndex)
        {
            throw new NotImplementedException("But our database is: " + Database);
        }
    }
}

Build your solution get Sitecore started and check your logs. You should now see an error similar to:

7472 09:09:42 ERROR Error loading hook: <hook type=”Sitecore.ContentSearch.Hooks.Initializer, Sitecore.ContentSearch” patch:source=”Sitecore.ContentSearch.config” xmlns:patch=”http://www.sitecore.net/xmlconfig/&#8221; />

Exception: System.Reflection.TargetInvocationException

Message: Exception has been thrown by the target of an invocation.

Nested Exception

 

Exception: System.NotImplementedException

Message: But our database is: web

Source: LaunchSitecore

at LaunchSitecore.IndexStrategy.MyUpdateStrategy.Initialize(ISearchIndex searchIndex) in c:\inetpub\wwwroot\SC75CommerceConnectDemo\Website\IndexStrategy\MyUpdateStrategy.cs:line 16

at Sitecore.ContentSearch.LuceneProvider.LuceneIndex.AddStrategy(IIndexUpdateStrategy strategy)

Since we’re intentionally throwing an exception this is what we should expect at this point. From here we can begin our actual implementation. As a sample use case we’ll configure our strategy to update the index for any items that a published item links to in our droptree field named “Foo”.

The first step will be to set up a handler to account for the publish end event. We’ll do this by using the EventHub.

EventHub.PublishEnd += (sender, args) => HandlePublishEnd(sender, args);  //Who knew such magic exsisted!

And, of course, we’ll need a HandlePublishEnd event.

  private void HandlePublishEnd(object sender, EventArgs args)
        {
            throw new NotImplementedException("Not yet...");
        }

Let’s stop again and test things. Build your solution and publish an item in Sitecore. Be sure to re-publish rather than smart publish. Again, if everything is working so far we should see our error in the logs.

IndexStratErrorSS

We’re now running our code when a publish ends, however we’ll want to know the contents of that publish. That’s where Sitecore’s history engine comes into play.

Once we know we’re able to run our code on publish end, let’s register, then trigger, an action with the OperationMonitor.

  private void HandlePublishEnd(object sender, EventArgs args)
        {
            OperationMonitor.Register(new Action(this.Run));
            OperationMonitor.Trigger();
        }

        public void Run()
        {
	…
}

For my example I’ll implement the run method by getting the database from the name specified in the config, reading from the database history, extracting items from our “Foo” field, then using the IndexCustodian to refresh said items.

  public void Run()
        {
            CrawlingLog.Log.Info(string.Format("[Index={0}] MyUpdateStrategy Publish ended", Index.Name), null);

            var database = Sitecore.Data.Database.GetDatabase(Database);
            if (database == null)
            {
                CrawlingLog.Log.Error(string.Format("[Index={0}] MyUpdateStrategy unable to find database: {1}", Index.Name, Database), null);
                return;
            }

            if (Index == null)
            {
                CrawlingLog.Log.Error(string.Format("[Index={0}] MyUpdateStrategy index is null", Index.Name), null);
                return;
            }

            var historyKey = string.Format("{0}{1}", Environment.MachineName, GetType().FullName);

            var historyItems = database.DatabaseHistory().GetLatestHistoryEntries(historyKey);
            var itemsToReIndex = historyItems.Select(x => x.ItemId)
                .Where(x => !x.IsNull)
                .Distinct()
                .Select(x => database.GetItem(x))
                .Where(x => TemplateManager.GetTemplate(x).DescendsFromOrEquals(TestTemplateID))
                .Select(x => ((Sitecore.Data.Fields.LookupField)x.Fields["Foo"]))
                .Where(x => x != null)
                .Select(x => x.TargetItem)
                .ToList();

            foreach (var indexableItem in itemsToReIndex)
            {
                CrawlingLog.Log.Info(string.Format("[Index={0}] MyUpdateStrategy refreshing {1}", Index.Name, indexableItem.Paths.FullPath), null);
                IndexCustodian.Refresh(Index, new SitecoreIndexableItem(indexableItem));
            }
        }

You should now be able to fully customize how and when an index is updated. You’ll be able to key off different Sitecore events, pull data from other sources and specify which items in the index should be refreshed.

Another great reference to use is Dan Cruickshank’s blog post.

Sitecore’s History Engine part 2

Last year I wrote a post quickly describing the use of Sitecore’s HistoryManager. Well a lot’s happened since then and I thought I’d post a quick update.

I’ve re-worked some of the code that’s been used and re-used as an extension of Sitecore’s Database class and thought I would share in case others find it as a useful starting point. The extension methods follow Daniel Cazzulino’s ideas on extension methods and testing. I’ll link to his blog post below (or here), it’s a good read, especially if you’re in a group that tends to go a little extension method crazy.
Before we get started I should probably mention that any database using the HistoryEngine will need to have it enabled. This is usually done via a config patch.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <databases>
          <database id="web">
            <Engines.HistoryEngine.Storage>
              <obj type="Sitecore.Data.SqlServer.SqlServerHistoryStorage, Sitecore.Kernel">
                <param connectionStringName="$(id)"/>
                <EntryLifeTime>30.00:00:00</EntryLifeTime>
              </obj>
            </Engines.HistoryEngine.Storage>
          </database>
        </databases>
      </sitecore>
    </configuration>

Now into the code…
First up we’re going to make an entry point extension method for our code.

public static class DatabaseExtensions
    {
        internal static Func<Database, IDatabaseHistory> DatabaseHistoryFactory = x => new DatabaseHistory(x);

        public static IDatabaseHistory DatabaseHistory(this Database db)
        {
            return DatabaseHistoryFactory(db);
        }
    }

This will allow you to change out the DatabaseHistoryFactory while running tests in order to supply a mock IDatabaseHistory that doesn’t have a real database dependency. It also has the added feature of allowing us to group our extension method so intellisense doesn’t look like a dog’s breakfast by the end of a project.
Next we’ll create our IDatabaseHistory interface. This defines 4 methods used to manage history entries.

public interface IDatabaseHistory
    {
        /// <summary>
        /// Gets the newest history entries since the last read.
        /// </summary>
        /// <param name="key">The key used while storing and retrieving the last updated date from the dataabase</param>
        /// <returns></returns>
        IEnumerable<HistoryEntry> GetLatestHistoryEntries(string key);

        /// <summary>
        /// Returns all history entries that match the supplied parameters.
        /// </summary>
        /// <param name="startTime">The earliest hisory entry we're interested in. Defaults to DateTime.MinValue</param>
        /// <param name="endTime">The last history entry we're interested in.  Defaults to DateTime.Now</param>
        /// <param name="category">Limits the results to HisotryEntries of this HistoryCategory.  Defaults to HistoryCategory.Item</param>
        /// <returns></returns>
        IEnumerable<HistoryEntry> ProcessHistory(DateTime? startTime = null, DateTime? endTime = null, HistoryCategory category = HistoryCategory.Item);

        /// <summary>
        /// Records the last read date for the given key.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="startTime"></param>
        void SetLastReadDate(string key, DateTime? startTime = null);

        /// <summary>
        /// Retrieves the last read date for the given key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        DateTime GetLastRecordedUpdateTime(string key);
    }

And finally we’ll create an implementation of this interface. This is the code where we’ll actually be reading from the database and returning results

public class DatabaseHistory : IDatabaseHistory
    {
        public Database Database { get; set; }

        public DatabaseHistory(Database database)
        {
            Assert.ArgumentNotNull(database, "Database");
            Database = database;
        }

        public IEnumerable<HistoryEntry> GetLatestHistoryEntries(string key)
        {
            Assert.ArgumentNotNull(key, "key");
            Assert.IsNotNull(Database, "Unable to process history from null database");
            DateTime fromDate = GetLastRecordedUpdateTime(key);
            DateTime toDate = DateTime.UtcNow;

            IEnumerable<HistoryEntry> results;
            try
            {
                results = ProcessHistory(fromDate, toDate);
            }
            catch (Exception ex)
            {
                Log.Error("Error reading database history", ex, this);
                return Enumerable.Empty<HistoryEntry>();
            }

            SetLastReadDate(key, toDate);

            return results;
        }

        public IEnumerable<HistoryEntry> ProcessHistory(DateTime? startTime = null, DateTime? endTime = null, HistoryCategory category = HistoryCategory.Item)
        {
            Assert.IsNotNull(Database, "Unable to process history from null database");

            DateTime fromDate = startTime.HasValue ? startTime.Value : DateTime.MinValue;
            DateTime toDate = endTime.HasValue ? endTime.Value : DateTime.UtcNow;

            var historyEntries = HistoryManager.GetHistory(Database, fromDate, toDate)
                                        .Where(x => x.Category == category);

            return historyEntries;
        }

        public void SetLastReadDate(string key, DateTime? startTime = null)
        {
            Assert.ArgumentNotNullOrEmpty(key, "key");
            Assert.IsNotNull(Database, "Unable to process history from null database");

            var utcNow = startTime.HasValue ? startTime.Value : DateTime.UtcNow;

            // writing back the date flag of our last operation
            Database.Properties[key] = DateUtil.ToIsoDate(utcNow, true);
        }

        public virtual DateTime GetLastRecordedUpdateTime(string lastUpdatePropertyName)
        {
            Assert.IsNotNull(Database, "Unable to process history from null database");
            DateTime lastUpdateTime;

            if (!DateTime.TryParse(Database.Properties[lastUpdatePropertyName], out lastUpdateTime))
            {
                lastUpdateTime = DateUtil.ParseDateTime(Database.Properties[lastUpdatePropertyName], DateTime.MinValue);
            }

            return lastUpdateTime;
        }
    }

From here you’re all set to go. An example use would be:

Sitecore.Context.Database.DatabaseHistory().GetLatestHistoryEntries("myKey")

One thing I’d like to mention before I go is to pick your key carefully. A common use case I’ve found for the history engine is to find all of the items that have been published since the last time a task was run. If the same key is used in a load balanced environment the first server to run the code will also update the last read data for that key, leaving the other servers with nothing. In this case I’ve found it helpful to append either a specific configuration setting, unique to each web server or something like Environment.MachineName.

Other reading:
http://blogs.clariusconsulting.net/kzu/making-extension-methods-amenable-to-mocking/
https://blog.horizontalintegration.com/2014/03/21/a-brief-look-at-the-sitecore-historymanager/

Creating a new Sitecore Rules Macro in Sitecore 8

A long time ago I wrote a post to help developers get started using the Sitecore rules engine.  I’d like to continue that conversation now by demonstrating how to create a new macro for use in your actions and conditions, if you haven’t read the previous entry I strongly recommend doing so now.  The macro used in a condition or rule will control the dialog presented to an end user that is configuring that rule.  There’s a good list and descriptions of them found here.  Macros are referenced in the text field of actions and conditions and will end up looking something like “Where blah blah blah [SearchTerm,NewMacro,/sitecore/content/home,value] blah.”  The […] will be presented to and end user as a link and the contents of the bracket control its action and display.  The first parameter is the property of the class that will be set when the action is executed.  The second parameter is the macro to use, in our case a newly created macro named “NewMacro”.  The third parameter will be passed into the macro and can be used to further control functionality.  These will be converted to a Sitecore Url string and allow you to vary the performance of the macro based on the condition or action it’s used on.  The final parameter is the default value displayed to the content author.

When creating a new macro for use in Sitecore rules the first thing you’ll want to do is create a new macro item in /sitecore/system/Settings/Rules/Definitions/Macros.  You’ll also want to create class in your solution and reference the class in the newly created marco’s Type field.

image

 

The class you create will need to implement Sitecore’s Sitecore.Rules.RuleMacros.IRuleMacro interface.  There’s a single method defined in this interface.  This method is called when a user clicks the macro’s link in the rule editor.  The parameter’s that are passed in provide the xml defining the condition or action, the property name that will be set, the url string specified as the 3rd parameter in the action or condition and finally the current value.

As a proof of concept I’ve created a simple implementation.

public void Execute(System.Xml.Linq.XElement element, string name, Sitecore.Text.UrlString parameters, string value)
{
var options = new SelectItemOptions()
{
Title = "Foo",
Text = "Bar",
Icon = "People/16x16/cube_blue.png",
ResultType = SelectItemOptions.DialogResultType.Id,
Root = Sitecore.Context.ContentDatabase.SelectSingleItem(parameters["root"]),
};

options.IncludeTemplatesForSelection = SelectItemOptions.GetTemplateList(Sitecore.Context.ContentDatabase, new[] { "{E6964C3E-D415-40C8-91D9-2BF90F6566E9}", "{1B6A3702-5694-4FC4-8366-989ECDCD7F1B}", "{826DC4A0-BEB8-4774-8FA7-791F1EC584B4}" });
options.IncludeTemplatesForSelection = SelectItemOptions.GetTemplateList(Sitecore.Context.ContentDatabase, new[] { "{E6964C3E-D415-40C8-91D9-2BF90F6566E9}", "{1B6A3702-5694-4FC4-8366-989ECDCD7F1B}" });
SheerResponse.ShowModalDialog(options.ToUrlString().ToString(), true);
}

 

This macro can now be set up for use on your conditions or actions.  I’ve added a new condition to an existing rule element folder, shown below.

image

 

Once we build our solution we’re all set to go.  Opening the rule editor now shows our new condition using our custom macro.

image

Clicking the “value” link will now execute our NewMacro.Execute(…) method.  Displaying a Sheer dialog using the parameters specified in the condition.  Test content from LaunchSitecore.

image

Syncing divisions from an external source with Sitecore Commerce Connector

Today we’re going to set up a simple import process to start syncing divisions using Sitecore Commerce Connect.  While this post will focus on syncing divisions, it could be easily adapted for use on other commerce entities.  I’m also going to assume that you already have a working Sitecore 7.5 installation with the Commerce Connect installed and set up.

The first thing to know when setting up a division sync process is that the default Commerce Connect defines a sync pipeline.  We’re going to insert some custom code into this pipeline to mock up retrieving division listings from an external source and saving these to our Sitecore instance. Right now we’re going to focus on syncing in a single direction, if you’ll be pushing changes made in Sitecore back out there are a few additional considerations to keep in mind, these will be covered at a later date.

Let’s get started.  First we’ll create a folder in the App_Config\Include called “CommerceConnectTest” or something clever like that. Creating this folder will help ensure that your configuration files are run in the correct order.  In here we’ll add a .config file to patch into the Commerce Connect sync pipelines and remove what isn’t needed for a single direction sync.  While we’re at it, let’s also create a new class for the division sync pipeline process we’re about to create.

The thing to keep in mind when you patch in a new processor is that you’ll want to make sure it runs before the save divisions processor.  In the end it should look something like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <commerce.synchronizeProducts.synchronizeDivisions>
        <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeDivisions.ReadSitecoreDivisions, Sitecore.Commerce">
          <patch:delete />
        </processor>
        <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeDivisions.ResolveDivisionsChanges, Sitecore.Commerce">
          <patch:delete />
        </processor>
        <processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeDivisions.SaveDivisionsToExternalCommerceSystem, Sitecore.Commerce" >
          <patch:delete />
        </processor>
        <processor type="CommerceConnectTest.DivisionSync, CommerceConnectTest" patch:after="processor[@type='Sitecore.Commerce.Pipelines.Products.SynchronizeDivisions.ReadExternalCommerceSystemDivisions, Sitecore.Commerce']" />
      </commerce.synchronizeProducts.synchronizeDivisions>
    </pipelines>
  </sitecore>
</configuration>

After patching your …/sitecore/admin/showconfig.aspx should contain something like this.

<commerce.synchronizeProducts.synchronizeDivisions patch:source="Sitecore.Commerce.Products.config">
<!--
 The following processor can be removed if synchronization will be performed only from external commerce system to Sitecore
-->
<processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeDivisions.ReadExternalCommerceSystemDivisions, Sitecore.Commerce"/>
<processor type="CommerceConnectTest.DivisionSync, CommerceConnectTest" patch:source="DivisionSync.config"/>
<!--
 The following processor can be removed if synchronization will be performed only from external commerce system to Sitecore
-->
<!--
 The following processor can be removed if synchronization will be performed only from external commerce system to Sitecore
-->
<processor type="Sitecore.Commerce.Pipelines.Products.SynchronizeDivisions.SaveDivisionsToSitecore, Sitecore.Commerce">
<param desc="divisionRepository" ref="divisionRepository"/>
</processor>
</commerce.synchronizeProducts.synchronizeDivisions>

At this point we’ve inserted some custom code into the synchronizeDivisions pipeline, however our DivisionSync class isn’t quite ready to go yet.  In order for Sitecore to know how to interact with this class we’ll need to have it extend

PipelineProcessor<ServicePipelineArgs>

and implement the Process method.

According to the documentation we’ll need to use the “Divisions” property of the request to add our external divisions. This is an enumeration of Division entities. How that division entity is configured and created is pretty slick, if you’re this far looking to do some research of your own into Commerce Connect I recommend looking into the repositories further.  Depending on what other processes you have running in this pipeline it may not be created for you already.  If it doesn’t exist (it shouldn’t in this example) create it and add a test division.  With the magic of copy and paste your process method should look like this:


public override void Process(ServicePipelineArgs args)
        {
            try
            {
                if (!args.Request.Properties.Contains("Divisions"))
                {
                    args.Request.Properties["Divisions"] = new List<Division>();
                }

                List<Division> divisions = args.Request.Properties["Divisions"] as List<Division>;
                if (divisions == null)
                {
                    Log.Error("Oops!", this);
                    args.Result.Success = false;
                    return;
                }

                divisions.Add(new Division
                {
                    Name = "My First division " + DateTime.Now.Second.ToString(),
                    Created = DateTime.MinValue,
                    Updated = DateTime.Now,
                    ExternalId = "42",
                    Description = "Hello World!",
                });

                args.Result.Properties["Divisions"] = divisions;

                args.Result.Success &= true;
            }
            catch (Exception ex)
            {
                Log.Error("Error syncing divisions", ex, this);
                args.Result.Success = false;
            }
        }

Let’s save, build and run Synchronize artifacts again.  Once it completes navigate to or refresh the divisions in Sitecore.  You should now see a newly created division.

SitecoreDivision

The creation of the Sitecore items from the contents of the Divisions property is handled in the SaveDivisionsToSitecore process, which is setup in the default installation.  Not having to re-create the Sitecore CRUD operations is a pretty nice time-saver.  Just remember that in order to take advantage of it you’ll need to snuggle your processor in before the SaveDivisionsToSitecore processor.

I hope you found this helpful and a good starting place.  Check back often for new updates on Sitecore Commerce Connect