0 comments on “Geocoding Australian postcodes”

Geocoding Australian postcodes

While working on some code that allowed a user to perform a search using only a postcode, I discovered some strange behaviour with the Google Maps API.

According to the Google Maps API documentation (https://developers.google.com/maps/documentation/geocoding/intro#ComponentFiltering), component filtering allows you to filter by postal_code and country, which would suit this need perfectly. I gave this a try, and upon initial testing, it seemed that this was the solution, however, after further testing, it was found that for some postcodes (specifically, some in NSW and ACT), the geocoding query would return ZERO_RESULTS. Maybe this is because there is an overlap in the 2000 series postcodes ¯\_(ツ)_/¯.

An example of the URL I was using for this is shown below (note that postcode 2022 and country AU will return ZER0_RESULTS):

http://maps.googleapis.com/maps/api/geocode/json?components=country%3aAU%7Cpostal_code:2022&sensor=false&client=your_client&channel=web&language=en&signature=your_signature

There are many examples on Stack Overflow of people using this format to search by postcode and claim this to be the solution, but most of them are either from other countries, where this probably isn’t an issue, or they mustn’t have discovered this issue.

According to the Google Maps API documentation, you can use administrative_area (among other fields) to “influence” results, so I tried adding the state to this field, and I found that this made everything work properly. That means that the following URL will geocode the postcode 2022:

http://maps.googleapis.com/maps/api/geocode/json?&components=country%3aAU%7Cpostal_code%3a2022%7Cadministrative_area%3aNSW&sensor=false&client=your_client&channel=web&language=en&signature=your_signature

The issue I had then was that if the user is searching only using the postcode, I had to find a way to provide the state for that postcode to Google so it could geocode the postcode properly. To do this, I created a function that gives me the state based on a postcode as shown below (although this is not a perfect solution, because new postcodes are added from time to time. Potentially a call to an Australia Post API or similar may work better going forward):


public static string PostcodeToState(int postcode)
{
var postcodes = new List();
postcodes.AddRange(Enumerable.Range(1000,1000).Select(x => new KeyValuePair("NSW",x)));
postcodes.AddRange(Enumerable.Range(2000, 600).Select(x => new KeyValuePair("NSW", x)));
postcodes.AddRange(Enumerable.Range(2619, 280).Select(x => new KeyValuePair("NSW", x)));
postcodes.AddRange(Enumerable.Range(2921, 79).Select(x => new KeyValuePair("NSW", x)));

postcodes.AddRange(Enumerable.Range(200, 100).Select(x => new KeyValuePair("ACT", x)));
postcodes.AddRange(Enumerable.Range(2600, 19).Select(x => new KeyValuePair("ACT", x)));
postcodes.AddRange(Enumerable.Range(2900, 21).Select(x => new KeyValuePair("ACT", x)));

postcodes.AddRange(Enumerable.Range(3000, 1000).Select(x => new KeyValuePair("VIC", x)));
postcodes.AddRange(Enumerable.Range(8000, 1000).Select(x => new KeyValuePair("VIC", x)));

postcodes.AddRange(Enumerable.Range(4000, 1000).Select(x => new KeyValuePair("QLD", x)));
postcodes.AddRange(Enumerable.Range(9000, 1000).Select(x => new KeyValuePair("QLD", x)));

postcodes.AddRange(Enumerable.Range(5000, 1000).Select(x => new KeyValuePair("SA", x)));

postcodes.AddRange(Enumerable.Range(6000, 798).Select(x => new KeyValuePair("WA", x)));
postcodes.AddRange(Enumerable.Range(6800, 200).Select(x => new KeyValuePair("WA", x)));

postcodes.AddRange(Enumerable.Range(7000, 1000).Select(x => new KeyValuePair("TAS", x)));

postcodes.AddRange(Enumerable.Range(800, 200).Select(x => new KeyValuePair("NT", x)));

postcodes.Add(new KeyValuePair("ACT", 2620));
postcodes.Add(new KeyValuePair("NSW", 3644));
postcodes.Add(new KeyValuePair("NSW", 3707));

return postcodes.Where(x => x.Value == postcode).Select(x => x.Key).FirstOrDefault();
}

 

0 comments on “Congratulations! You’re Sitecore Certified”

Congratulations! You’re Sitecore Certified

Our developers are Sitecore 9 certified

We’d like to congratulate all our developers who’ve achieved Sitecore® 9.0 Certified Platform Associate Developer certification so far.

Sitecore’s certification exams validate the skills and knowledge of developers, marketers and business users. Test takers who pass the certification exams earn the distinction of being Sitecore Certified Professionals.

At Aceik we work exclusively in Sitecore and have formed a strategic relationship with Sitecore. All our developers are Sitecore certified and work towards certification in the latest Sitecore, are highly qualified and totally dedicated to their projects.

Please contact us for more details or if you want to discuss a project.  All enquiries should be made to info@aceik.com.au or +61 (0)4 2697 1867

 

 

0 comments on “Sitecore Page Speed: Part 3: Eliminate JS Loading Time”

Sitecore Page Speed: Part 3: Eliminate JS Loading Time

In part 1 & part 2 of our Sitecore page speed blog, we covered off:

  • The Google Page Speed Insights tool.
  • We looked at a node tool called critical that could generate above the fold (critical viewport) CSS code that is minified.
  • We referenced the way in which Google recommends deferring CSS loading.
  • We showed a way to integrate “Above The Fold” CSS into a Helix based project and achieve a page free of render blocking CSS.

In this 3rd part of the series, we will introduce a way to defer the load of all external javascript assets (async).

A reminder that I have committed the sample code for this blog into a fork of the helix habitat example project. You can find the sample here. For a direct comparison of changes made to achieve these page load enhancements, view a side by side comparison here.

Dynamic JS loading Installation Steps:

  1. Inside Sitecore add a new view Rendering that reference the file /Views/Common/Assets/Scripts-3.2.1.cshtml
    • Note down the ID of this rendering and replace in the ID of the rendering in the next step.
  2. Update the Default.cshtml layout to include a new cached rendering.
  3.  @*Scripts Legacy Jquery jquery-3.2.1 *@
     @Html.Sitecore().CachedRendering("{B0DD36CE-EE4A-4D01-9986-7BEF114196DD}", new RenderingCachingSettings { Cacheable = true, CacheKey = cacheKey + "_bottom_scripts" })
    • cacheKey = This variable is something unique that will identify the page. You could use the Sitecore context Item ID or path for example.

Explanation:

The rendering Scripts-3.2.1.cshtml will render out the following javascript onto the page:

var scriptsToLoad = ['//cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js','//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js','/assets/js/slick.min.js','/assets/js/global.js','/assets/js/Script.js','/assets-legacy/js/lib/lazyload.min.js'];
src="/assets-legacy/js/lib/jquery-3.2.1.min.js" async defer>
  • First of all, it prints out a JS array of all the scripts that this page requires.
    • This is the array of JS files that comes from Themes and Page Assets inside the CMS. If you are familiar with Habitat Helix this list can be content managed inside the CMS.
  • It then instructs the jquery library to be loaded async (which will not block the network download of the page response).
  • Once jquery is loaded, this modified version of jquery contains some code at the end that will read in the list of scripts dynamically and apply them to the page.
    • This is achieved with fairly simple AJAX load calls to the script URLs.

Outcome:

Once integrated successfully you will end up with a page that does not contain any blocking JS network calls.  The Google Page Speed tool should give you a nice score boost for your achievement in reducing initial load time.


Hints and Tips:

Bootstrapping JQuery Code:

  • jquery Document.Ready() function calls may not fire inside dynamically loaded JS files. This is because the JS file is loaded after DOM is ready and it’s too late for the Document.Ready() event at this stage.
  • As a workaround, you could code your JS files to bootstrap on both the Document.Ready() or whenever $ is not undefined.
  • In the case of dynamic loading in this manner, because jquery was loaded first, $ should not be undefined and your code should be bootstrapped successfully.

Debugging in chrome:

  • When dynamically loading JS files they may strangely not appear in the chrome console debugger as you would normally expect.
  • The workaround for this is to add a comment to the top of each JS library
  • //# sourceURL=global.js
  • This will cause the chrome debugger to list the file in the source tab under the “(no domain)” heading.
  • You will then be able to debug the file as per normal.
0 comments on “Aceik announces FuseIT (S4S) partnership”

Aceik announces FuseIT (S4S) partnership

Aceik works directly with FuseIT, principally by aiding customers in making the link between their Sitecore visits and their Salesforce contacts through FuseIT’s industry standard S4S (Sitecore – Salesforce) connector. We handle the implementation so that the clients’ Sitecore analytics are surfaced in Salesforce, allowing the sales team to track the behavior of individual website visitors then personalize their experience.

 

About FuseIT

FuseIT is a private company founded in New Zealand in 1992. FuseIT has a history of building enterprise content management systems but our real forte is complex systems integration. Our qualified and highly capable team of professionals have proven experience in consulting, business analysis, solution architecture, software development, integration and innovation.

 

About Aceik

We’re a tight-knit team of Melbourne developers who are true craftspeople, creating cutting-edge solutions with the latest Sitecore technology, solving business problems while forging long-term client connections.

0 comments on “Aceik announces Stackla partnership”

Aceik announces Stackla partnership

Aceik enjoys a partnership with Stackla, the leading user-generated content (UGC) platform for enterprise brands. We facilite Australian integrations of Stackla into the Sitecore Experience platform so that our customers can deliver highly personalized brand experiences with fresh, real-time content.

 

About Stackla

Stackla is the world’s smartest visual content engine, helping modern marketers discover, manage and display the best content across all their marketing touchpoints.

With our AI-powered user-generated content (UGC) platform and asset manager, Stackla sits at the core of the marketing stack, actively discovering and recommending the best visual content from across the social web, as well as internal and external resources, to fuel personalized content experiences at scale.

Using Stackla, marketers can spend less time creating expensive creative assets and more time delivering relatable and influential visual customer experiences.

 

About Aceik

We’re a tight-knit team of Melbourne developers who are true craftspeople, creating cutting-edge solutions with the latest Sitecore technology, solving business problems while forging long-term client connections.

0 comments on “Sitecore Azure Search: Top 10 Tips”

Sitecore Azure Search: Top 10 Tips

Its been a while since I first wrote about Azure Search and we have a few more tips and tricks on how to optimise Azure Search implementations.

Before proceeding if you missed our previous posts check out some tools we created for Azure Search Helix setup and Geo-Spatial Searching.

Also, check out the slides from our presentation at last years Melbourne Sitecore User Group.

Ok let us jump into the top 10 tips:

Tip 1) Create custom indexes for targeted searching

The default out of the box indexes will attempt to cover just about everything in your Sitecore databases. They do so to support Sitecore CMS UI searches out of the box.  It’s not a problem if you want to use the default indexes (web, master) to search with, however for optimal searches and faster re-indexing time a custom index will help performance.

By stepping back and looking at the different search requirements across the site you can map out your custom indexes and the data that each will require.

Consider also that if the custom indexes need to be used across multiple Feature Helix modules the configuration files and search repositories may need to live in an appropriate Foundation module. More about feature vs foundation can be found here.

Tip 2) Keep your indexes lean

This tip follows on from the first Tip.

Essentially the default Azure Search configuration out of the box will have:

<indexAllFields>true</indexAllFields>

This can include a lot of fields and your probably not going to need every single Sitecore field in order to present the user with meaningful data on the front end interfaces.

The other option is to specify only the fields that you need in your indexes:

<include hint="list:IncludeField"> 
<Text>{A60ACD61-A6DB-4182-8329-C957982CEC74}</Text> 
</include>

The end result will limit the amount of JSON payload that needs to be sent across the network and also the amount of payload that the Sitecore Azure Search Provider needs to process.

Particularly if you are returning thousands of search results you can see what happens when “IndexAllFields” is on via Fiddler.

This screenshot is via a local development machine and Azure Search instance at the Microsoft hosting centre.

Fiddler Index

JSONFIelds

  • So for a single query “IndexAllFields” can result in:
    • 2 MB plus JSON payload size.
    • Document results with all Sitecore metadata included. That could be around 100 fields.

If your query results in Document counts in the thousands obviously the payload will grow rapidly. By reducing the fields in your indexes (removing un-necessary data)  you can speed up query, transfer and processing times and get the data displayed quicker.

Tip 3) Make use of direct azure connections

Sitecore has done a lot of the heavy lifting for you in the Sitecore Azure Search Provider. It’s a bit like a wrapper that does all the hard work for you. In some cases however you may find that writing your own queries that connect via the Azure Search DLL gives you better performance.

Tip 4) Monitor performance via Azure Search Portal

It’s really important to monitor your Azure Search Instance via Azure Portal. This will give you critical clues as to whether your scaling settings are appropriate.

In particular look out for high latency times as this will indicate that your search queries are getting throttled. As a result, you may need to scale up your Azure Search Instance.

In order to monitor your latency times go to:

  1. Login to Azure Portal
  2. Navigate to your Azure Search Instance.
  3. Click on metrics in the left-hand navigation
    • metrics
  4. Select the “Search Latency” checkbox and scan over the last week.
    • graph
  5. You will see some peaks these usually indicate heavy periods of re-indexing. During re-indexing, the Azure Search instance is under heavy load. As long as your peaks under 0.5-second mark your ok.  If you see Search Latency up into the 2-second timeframe you probably need to either adjust how your indexes are used (caching and re-indexing) or scale up to avoid the flow on effects of slow search.

Tip 5) Cache Wrappers

In the code that uses Azure Search, it would be advisable to use cache wrappers around the searches when possible. For your most common searches, this should prevent Azure Search getting hit repeatedly with the same query.

For a full example of cache wrapper checkout the section titled Sitecore.Caching.CustomCache in my previous blog post.

Tip 6) Disable Indexing on CD

This is a hot tip that we got from Sitecore Support when we started to encounter high search latency during re-indexing.

Most likely in your production setup, you will have a single Azure Search instance shared between CM and CD environments.

You need to factor in that CM should be the server that controls the re-indexing (writing) and CD will most likely be the server doing the queries (reading).

Re-indexing is triggered via the event queue and every server subscribes and reacts to these events. Each server with the out of the box search configuration will cause the Azure Search indexes to be updated.  In a shared Azure Search (or SOLR instance) this only needs to be updated by a single server. Each additional re-index is overkill and just doubling up on re-indexing workload.

You can, therefore, adjust the configuration on the CD servers so that it does not cause re-indexing to happen.

The trick is in your index configuration files to use Configuration Roles to specify the indexing strategy on each server.

 <strategies hint="list:AddStrategy">
 <!--
 NOTE: order of these is controls the execution order 
 -->
 <strategy role:require="Standalone OR ContentManagement" ref="contentSearch/indexConfigurations/indexUpdateStrategies/onPublishEndAsync"/>
 <strategy role:require="ContentDelivery" ref="contentSearch/indexConfigurations/indexUpdateStrategies/manual"/>
 </strategies>

Setting the index update strategy to manual on your CD servers will take a big load off your remote indexes.

Particularly if you have multiple CD servers using the same indexes. Each additional CD server would cause additional updates to the index without the above setting.

Tip 7) Rigid Indexes – Have a deployment plan

If your deployment includes additions and changes to the indexes and you need 100% availability of search data, a deployment plan for re-indexing will be required.

Grant chatted about the problem in his post here. To get around this you could consider using the blue / green paradigm during deployments.

  • This would mean having a set blue indexes and a set of green indexes.
  • Using slot swaps for your deployments.
    • One slot points to green in configuration.
    • One slot (production) points to blue in configuration.
  • To save on costs you could decommission the staging slot between deployments.

Tip 8) HttpClient should be a singleton or static

The basic idea here is that you should keep the number of HttpClient instances in your code to an absolute minimum if you want optimal performance.

The Sitecore Azure Search provider actually spins up 2 x HttpClient connections for every single index. This in itself is not ideal and unfortunately, there is not a lot you can do about this code in the core product itself.

In your own connections to other APIs, however, HttpClient SendAsync is perfectly thread safe.

By using HttpClient singletons you stand to gain big in the performance stakes. One great blog article worth reading runs you through the performance benefits. 

It’s also worth noting that in the Azure Search documentation Microsoft themselves say you should treat HttpClient as a singleton.

Tip 9) Monitor your resources

In Azure web apps you have finite resources with your app server plans. Opening multiple connections with HttpClient and not disposing of them properly can have severe consequences.

For instance, we found a bug in the core Sitecore product that was caused by the connection retryer. It held open ports forever whenever we hit out Azure Search plan usage limits.  The result was that we hit outbound open connection limits for sockets and this caused our Sitecore instance to ground to a slow halt.

Sitecore has since resolved the issue mentioned above after a lengthy investigation working alongside the Aceik team. This was tracked under reference number 203909.

To monitor the number of sockets in Azure we found a nice page on the MSDN site.

Tip 10) Make use of OData Expressions

This tip relates strongly to tip 3.  Azure search has some really powerful OData Expressions that you can make use of by a direct connection.  Once you have had a play with direct connections it is surprisingly easy to spin up really fast queries.

Operators include:

  • OrderBy, Filter (by field), Search
  • Logical operators (and, or, not).
  • Comparison expressions (eq, ne, gt, lt, ge, le).
  • any with no parameters. This tests whether a field of type Collection(Edm.String) contains any elements.
  • any and all with limited lambda expression support.
  • Geospatial functions geo.distance and geo.intersects. The geo.distance function returns the distance in kilometres between two points.

See the complete list here.


 

Q&A

Q) Anything on multiple region setups? Or latency considerations?

A) Multi-region setups:   Although I can’t comment from experience the configuration documentation does state that you can specify multiple Azure Search instances using a pipe separator in the connection string.

<add name="cloud.search" connectionString="serviceUrl=https://searchservice1.search.windows.net;apiVersion=2015-02-28;apiKey=AdminKey1|serviceUrl=https://searchservice2.search.windows.net;apiVersion=2015-02-28;apiKey=AdminKey2" /> 

Unfortunately, the documentation does not go into much detail. It simply states that “Sitecore supports a Search service with geo-replicated scenarios” which one would hope means under the hood it has all the smarts to take care of this.

I’m curious about this as well and opened a stack overflow ticket. Let’s see if anyone else in the community can answer this for us.

Search Latency: 

Search latency can be directly improved by adding more replicas via the scaling setting in Azure Portal

replicas

Two replicas should be your starting point for an Azure Search instance to support Sitecore. Once you launch your site you will need to follow the instruction in tip 4 above monitor search latency.  If the latency graph is showing consistent spikes and high latency times above 0.5 seconds it’s probably time to add some more replicas.

0 comments on “Aceik’s Jason Horne Wins Sitecore “Most Valuable Professional” Award 2018”

Aceik’s Jason Horne Wins Sitecore “Most Valuable Professional” Award 2018

Sitecore_MVP_logo_Technology_2018.jpgElite distinction awarded for exceptional contributions to the Sitecore ecosystem

Melbourne, Victoria, Australia — January 31, 2018 — Aceik – Sitecore Specialists, today announced that Jason Horne, Director and Chief Architect has been named a “Most Valuable Professional (MVP)” in Technology by Sitecore®, the global leader in experience management software. Jason Horne was one of only 208 Technology MVPs worldwide to be named a Sitecore MVP this year.

Now in its 12th year, Sitecore’s MVP program recognizes individual technology, strategy, and commerce advocates who share their Sitecore passion and expertise to offer positive customer experiences that drive business results. The Sitecore MVP Award recognizes the most active Sitecore experts from around the world who participate in online and offline communities to share their knowledge with other Sitecore partners and customers.

Aceik has a very close and strong relationship with Sitecore. We are very honored to be given this award in recognition of our expertise and contributions to Sitecore. Aceik is highly active in the Sitecore community providing thought leadership, presentations, workshops, open source modules, blog posts, co-founding and managing the Sitecore Melbourne user group and more. Through our deep engagement in the Sitecore community, Aceik stays on the cutting edge allowing us to provide the best advice and outcomes for our clients.

Aceik was founded in 2013 by Jason Horne as a Sitecore specialist consultancy (About Us, Services). Aceik is a one of a kind Australian Sitecore partner. We focus on Sitecore, .Net, Azure, and Agile, keeping this dedicated and deliberate focus allows us to stay on top of a very fast-paced industry. We strive to provide excellence in every Sitecore project we undertake.

Some things that make us unique Include:

  • 100% of our work is Sitecore related.
  • We guarantee Sitecore experts on every project.
  • Speak directly with our technical team.
  • We care about all our clients, they are our biggest advocates
  • We are responsive and enthusiastic about all projects big or small
  • We deliver quality solutions at the highest level, honestly and transparently

“The Sitecore MVP awards recognize and honor those individuals who make substantial contributions to our loyal community of partners and customers,” said Pieter Brinkman, Sitecore Senior Director of Technical Marketing. “MVPs consistently set a standard of excellence by delivering technical chops, enthusiasm, and a commitment to giving back to the Sitecore community. They truly understand and deliver on the power of the Sitecore Experience Platform to create personalized brand experiences for their consumers, driving revenue and customer loyalty.”

The Sitecore Experience Platform™ combines web content management, omnichannel digital delivery, insights into customer activity and engagement, and strategic digital marketing tools into a single, unified platform. Sitecore Experience Commerce™ 9, released in January 2018, is the only cloud-enabled platform that natively integrates content and commerce so brands can fully personalize and individualize the end-to-end shopping experience before, during, and after the transaction. Both platforms capture in real time every minute interaction—and intention—that customers and prospects have with a brand across digital and offline channels. The result is that Sitecore customers are able to use the platform to engage with prospects and customers in a highly personalized manner, earning long-term customer loyalty.

 Aceiks mission is to deliver quality solutions honestly and transparently. Through our deep engagement in the Sitecore community and singular focus we provide a substantial return on investment to our clients with every engagement. We believe that clients and vendors can work together as a single team to deliver outstanding results as true partners in a common goal.

Please feel free to get in touch:
Jason Horne, Founder, and Sitecore specialist
Aceik
jasonhorne@aceik.com.au
0426971867

More information can be found about the MVP Program on the Sitecore MVP site: http://www.sitecore.com/mvp

0 comments on “Downgrading Helix modules from Sitecore 8.2 to 7.2”

Downgrading Helix modules from Sitecore 8.2 to 7.2

Recently I had to update a Sitecore 7.2 site to use Helix architecture and bring some foundation modules in from a Sitecore 8.2 site.  There were several challenges with this process.  Here’s a few highlights:

Dependency Injection using Castle Windsor

For this site, Castle Windsor was being used for dependency injection.  In the Sitecore 8.2 site, dependencies are registered using a configurator in a .config patch.  This is not supported in Sitecore 7.2, so instead we have to do this in the Application_Start event in global.asax.cs.

In order to be able to use the same DI container throughout the code, I created a singleton container object (ContainerManager.Container):

public static class ContainerManager
 {
   private static IWindsorContainer _container;
   public static IWindsorContainer Container
   {
     get
     {
        if (_container != null) return _container;
        _container = new WindsorContainer();
        _container.Install(FromAssembly.This());
        return _container;
     }
   }
}

Then in global.asax.cs, I used that container to register the dependencies:

protected void Application_Start(object sender, EventArgs e)
 {
    _container = ContainerManager.Container;
    _container.Install(new RegisterGlassDependencies());
    ...

An example of the installer class for registering dependencies is shown below:

 public class RegisterGlassDependencies : IWindsorInstaller
 {
   public void Install(IWindsorContainer container, IConfigurationStore store)
   {
     container.Register(Component.For<ISitecoreContext>().ImplementedBy<SitecoreContext>());
     container.Register(Component.For<IGlassHtml>().ImplementedBy<GlassHtmlTemp>());
     container.Register(Component.For<IGlassFactory>().ImplementedBy<GlassFactory>());
   }
 }

In order for injected constructor parameters to resolve for Controllers, it is necessary to use a custom controller factory which uses the DI container to resolve the controller. There’s a fair bit of overriding going on, so here it comes.

Code for the controller factory shown below:

public class WindsorControllerFactory : DefaultControllerFactory
   {
       private readonly IWindsorContainer _container;
 
       public WindsorControllerFactory(IWindsorContainer container)
       {
           _container = container;
       }
 
       public override void ReleaseController(IController controller)
       {
           _container.Release(controller);
       }
 
       public override IController CreateController(RequestContext requestContext,string controllerName)
       {
           Assert.ArgumentNotNull(requestContext, "requestContext");
           Assert.ArgumentNotNull(controllerName, "controllerName");
           Type controllerType = null;
 
           if (TypeHelper.LooksLikeTypeName(controllerName))
           {
               controllerType = TypeHelper.GetType(controllerName);
           }
 
           if (controllerType == null)
           {
               controllerType = GetControllerType(
                   requestContext,
                   controllerName);
           }
 
           if (controllerType != null)
           {
               return (IController)_container.Resolve(controllerType);
           }
 
           return base.CreateController(requestContext, controllerName);
       }
   }

A pipeline was added with an “instead” patch for Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory.  This pipleine scans all assemblies and gets all classes based on IController then registers them with the Containermanager.Container.  Code shown below:

public class InitializeWindsorControllerFactory
   {
       public virtual void Process(ScapiPipelineArgs args)
       {
           SetupControllerFactory(args);
       }
 
       protected virtual void SetupControllerFactory(ScapiPipelineArgs args)
       {
           var container = ContainerManager.Container;
 
           //TODO: don't use hard-coded filter string
           var assemblies = GetAssemblies.GetByFilter("MyAssembly.*").Where(n => !n.FullName.StartsWith("MyAssembly.Service"))
               .Where(a => GetTypes.GetTypesImplementing<IController>(a).Any(x => x.Namespace != null && x.Namespace.StartsWith("MyNamespace")));
 
           foreach (var assembly in assemblies)
           {
               container.Register(Classes.FromAssembly(assembly).BasedOn<IController>().LifestyleTransient());
           }
 
           var controllerFactory = new WindsorControllerFactory(container);
 
           var scapiSitecoreControllerFactory = new
               ScapiSitecoreControllerFactory(controllerFactory);
 
           ControllerBuilder.Current.SetControllerFactory(scapiSitecoreControllerFactory);
       }
   }

Another pipeline was added with an “instead” patch for Sitecore.Mvc.Pipelines.Response.GetRenderer.GetControllerRenderer.  This pipeline allows us to use a custom controller renderer, as shown below:

public class GetControllerRenderer : Sitecore.Mvc.Pipelines.Response.GetRenderer.GetControllerRenderer
   {
       protected override Renderer GetRenderer(Rendering rendering, Sitecore.Mvc.Pipelines.Response.GetRenderer.GetRendererArgs args)
       {
           var renderer = base.GetRenderer(rendering, args);
           return !(renderer is ControllerRenderer) ? renderer : new CustomControllerRenderer(renderer as ControllerRenderer);
       }
   }

The custom controller renderer then allows us to use a custom controller runner as shown below:

public sealed class CustomControllerRenderer : ControllerRenderer
   {
       public CustomControllerRenderer(ControllerRenderer renderer)
       {
           ControllerName = renderer.ControllerName;
           ActionName = renderer.ActionName;
       }
 
       public override void Render(System.IO.TextWriter writer)
       {
           var controllerName = ControllerName;
           var actionName = ActionName;
           if (controllerName.IsWhiteSpaceOrNull() || actionName.IsWhiteSpaceOrNull())
           {
               return;
           }
           var controllerRunner = new CustomControllerRunner(controllerName, actionName);
           var value = controllerRunner.Execute();
           if (value.IsEmptyOrNull())
           {
               return;
           }
           writer.Write(value);
       }
 
   }

The custom controller runner then creates the controller using our custom controller factory with our DI container as a parameter.  This is our goal. Custom controller runner shown below:

public class CustomControllerRunner : ControllerRunner
   {
       public CustomControllerRunner(string controllerName, string actionName)
           : base(controllerName, actionName)
       { }
 
       protected override IController CreateController()
       {
           return CreateControllerUsingFactory();
       }
 
       private IController CreateControllerUsingFactory()
       {
           NeedRelease = true;
 
           var controllerFactory = new WindsorControllerFactory(ContainerManager.Container);
           return controllerFactory.CreateController(PageContext.Current.RequestContext, ControllerName);
       }
   }

There’s a few layers to it, but we got there in the end.

XUnit Tests

The tests that I moved over from the Sitecore 8.2 site used XUnit.  After moving them over, one of the errors I got in the Sitecore 7.2 site was:

Could not resolve type name: Sitecore.Data.DefaultDatabase, Sitecore.Kernel

To fix this,  for Sitecore versions prior to 8.2 all instances of ‘Sitecore.Data.DefaultDatabase, Sitecore.Kernel’ in the config files should be changed to ‘Sitecore.Data.Database, Sitecore.Kernel’.

One bit I got stuck on for a while was that there was a config file with this string in it that had not yet been added to the solution, but was there on the file system, and that was enough for this error to still be thrown.  It took a file system search for the string to track it down.

 

 

0 comments on “JS & CSS Minification an Alternative Helix Approach”

JS & CSS Minification an Alternative Helix Approach

On a recent project, we were looking at a way to meet a number of criteria to improve page load times.

With regard to CSS and JS requests these criteria were:

  • Reduce the number of CSS and JS HTTP requests on page load.
  • Reduce the size of CSS and JS files (minification)

At the same time, we were running a Sitecore Helix project and we needed this to seamlessly fit into our project and CI builds.

In the past, I had also done some Umbraco development and was familiar with a nice little package called “ClientDependency Framework (CDF) by Shannon Deminick”  that comes out of the box with that particular CMS.

CDF will cut down your server requests in a few ways:

  • Combining  (It combines multiple JS files into a single server call on the fly)
  • compressing
  • minifying output
  • Composition
  • Caching (Processing of files into a composite file it cached)
  • Persisting the combined composite files for increased performance when applications restart or when the Cache expires

It also has a development mode were asset includes are not touched so that you can debug as usual.

The problem we had to solve was how to make the above package work in a way that it could plug and play with the Helix way of doing things.

The habitat (helix example project)  provides a way to include assets file via a global theme and also on an idividual rendering level.

Global themes should be used to load assets that are to be applied across a whole website.

Rendering level assets should be used to load css and javascript that only apply directly to that particular rendering.

The current habitat solution contains a module called “Assets” that lives in the foundation layer. This module will cleverly round up all the assets that need to be rendered on the page by the Helix Architecture.

In order to use ClientDependency Framework (CDF) and seamlessly plug it into the Assets module, we have provided two new layout tags for use:

Usage in Layouts
- @CompositeAssetFileService.Current.RenderStyles()
- @CompositeAssetFileService.Current.RenderScript(ScriptLocation.Body)

The above integration is currently on a pull request (waiting approval) on the main Helix Habitat GitHub  repository:

See: https://github.com/Sitecore/Habitat/pull/349

It will remain available on Aceik’s fork of the habitat project. 

Setup Steps: 

To use this service you will need to rename the following and run the gulp build:
/// – “App_Config/ClientDependency.config.disabled”. to App_Config/ClientDependency.config
/// – “\src\Foundation\Assets\code\Web.config.transform.ClientDependency.minification.example” to Web.config.transform

Once running (not in debug mode) you will see the following asset includes in the html source.

/DependencyHandler.axd/bdc200f5bb6df7e817066f4b98499322/12/js

A note about Cloudflare and minification: 

If your using cloudflare in front of your website,  JS and CSS minification can be turned on as feature in Cloudflare.  You can also compact the number of server requests made by using a feature called Rocket Loader. 

0 comments on “Understanding Form Submission Tracking (WFFM and xDB)”

Understanding Form Submission Tracking (WFFM and xDB)

This blog is targeted at a marketing audience that may be wondering how to interpret the WFFM tracking metrics, as shown in the form reports.

Definitions of metrics as proposed by Sitecore:

  • Visits – the total number of visitors who visited the page containing the web form.
  • Submission attempts – the total number of times that visitors clicked the submit button.
  • Dropouts – the total number of times that visitors filled in form fields but did not submit the form.
  • Successful submissions – the total number of times that visitors successfully submitted the web form and data was collected.

FormTracking

If you’re trying to test the above values by doing form submissions yourself and you don’t fully understand what is happening in the background, it will get confusing very quickly. Indeed some clients have asked me to investigate the tallies above as they believed the report was broken when some of the numbers started declining.

You really need to understand that just closing your browser when trying to affect the above metrics doesn’t fool the xDB into thinking your a different user.

Thanks to xDB cookies the values shown next to the metrics above can actually be reassigned.  What I mean by this is if a user is recorded as a dropout and they then return a few hours later to re-submit the form. The tally next to dropouts will actually reduce by one and that value will be added to successful submissions.  If you’re trying to test the tally for correctness a drop in some of these counts will leave you scratching your head.

The only guaranteed way for the user to be treated and a unique visitor is to clear your cookies and browser temp data.

A good way to perform these tests is to use “Incognito mode in chrome“. As this will prevent cookies from being stored.


This provides us with an explanation as to why you might see some of the tallies go into the negative. Which makes sense when you realise that dropouts can be converted to successful submissions.  How then do we explain when submission attempts drop in number?

Running a few tests on this reveals that when a user is converted from a drop-out to a successful submission, the submission attempts recorded against this user are also adjusted down.


The main takeaway from this is that xDB is clever and knows when the same user returns to a form.  It adjusts the tallies in the form report accordingly.

If your marketing department doesn’t care about how well a user is tracked and these tallies confuse them. Let’s say it wants to track the exact number of times a form was submitted (same user or not). You could achieve this independently just by tracking the number of times the thank-you page is loaded.