0 comments on “Top 5 Ways to Extend Sitecore HTML Cache”

Top 5 Ways to Extend Sitecore HTML Cache

In 2017 I wrote a reasonably long post on all the different considerations a Sitecore Caching Strategy might cover.

Following on from that post its time to share some custom HTML cache extensions that we at Aceik may incorporate into our projects. This count down of custom settings has been collected from across the Sitecore community.

5) Vary By Personalisation

This one (in my opinion) is a must-have if your incorporating personalisation in your homepage.

I admit it will only be effective up to a certain number of content variations and even then needs to be used with caution. Still, if you can save your server and databases from getting hit and help keep Time to First Byte (TTFB) low, its always worth it.

Please note that if you’re displaying a customised message with data that is only relevant to that user, the number of variations may not make it worthwhile.  On the other hand, if your showing variations based on a handful of profile card match rules, we found it to be fairly effective.

Code Sample: ApplyVaryByPeronalizedDatasource.cs

Credits: Ahmed Okour

4) Vary By Resolution

Its a fairly common scenario that we want to display a different image based on the users screen size. So it stands to reason that we would need a way to differentiate this when it comes to caching our Renderings.

The particular implementation was used in combination with Scott Mulligan’s Sitecore Adaptive Image Library.

The Adaptive Image library stores the users screen resolution via a cookie in the front end razor/javavscript:

document.cookie = '@Sitecore.Configuration.Settings.GetSetting("resolutionCookieName") =' + Math.max(screen.width, screen.height) + '; path=/';
  • The first time around if no cookie is set it uses the default largest image size as the cache key.
  • If the cookie is set the cache incorporates the screen resolution.
args.CacheKey += "_#resolution:" + AdaptiveMediaProvider.GetScreenResolution();

Code 1:  ApplyVaryByResolution.cs

Code 2: AdaptiveMediaProvider.cs 

Credit:  Dadi Zhao

3) Vary By Timeout

This one’s a little different, it requires not only a new checkbox but also a new “Single-Line text” field that allows you to enter a timeout value.  The idea, as you might have guessed, is for the rendering cache to expire after a certain amount of time.

Code 1: ApplyVaryByTimeout.cs

Credit: Dylan Young

2) Vary By Url

An oldy but a goody. I’m a little surprised this one just hasn’t made it into the out of the box product. On the other hand, I can see how it could be overused if you don’t understand the context it applies to. Essentially you can take either the Context Item ID or the raw URL and make your rendering cache vary based on that key.

A good use case for this setting could be for navigation that requires the current page to always be highlighted.

Code 1: ApplyVaryByURLCaching.cs  (Context Item ID formula)

Code 2: ApplyVaryByRawUrlCaching.cs (Raw URL formula)

Credit:  The 10 other people that have blogged about this over the years.

1) Vary By Website

Given Sitecore is an Enterprise content management system we often see multi-site implementations launched on the platform. It makes sense then that you have an option to cache renderings that don’t change all that much on one site but have different content on another.

Example Usage: A global navigation used across all sites that requires some content for the context site to show differently.

Code: ApplyVaryByWebsite.cs

Credit: Younes van Ruth

That rounds out the count down of some of the top ways to extend Sitecore’s out of the box rendering cache. Your renderings will likely use a combination of these settings in order to achieve adequate caching coverage.

For a better idea on how you might add the top 5 above into Sitecore. Please see the technical footnote below. 



 

Technical Footnote:

All these extensions will add an extra checkbox in the Rendering cache tab within Sitecore.

cachesettings

In order for this check box to show up you need to add your custom checkbox fields to the template:

/sitecore/templates/System/Layout/Sections/Caching

You can achieve this in several ways. and there are a lot of other blogs “on the line” that describe how to add in these custom checkboxes so I won’t go into a deep dive here.

With regard to the Helix architecture lets outline one way you could set this up. Aceik has a module in the foundation layer that has all the custom cache checkboxes added to a single template (serialized in Unicorn within that module) . The system template above is then made to inherit from your custom template in order to inherit the custom caching fields.

custom

1 comment on “Part 1 – Experience Profile – Identify Users Early”

Part 1 – Experience Profile – Identify Users Early

This is the first part of a four-part blog series where I will introduce some XDB customisation that could be of use on your next Sitecore project. All these customisations relate back to the Experience Profile and identifying the user.

Part 1:  We introduce the concept of early profile identification, there is no such thing as the anonymous user.

Part 2: We dive into the world of multi-site, multi-domain tracking. How to implement a global-common cookie for all your brand’s sites.

Part 3: External Tracking via FXM and Google Client ID – How to continue tracking a user on another website (not hosted in Sitecore).  (Release TBC)

Part 4: How to achieve Instant personalisation on the very first page load. We can make use of the fact that inbound links from a social stream can already identify a users demographic.


 

Part 1:  Identify Users Early

Some of this blog has not been updated for Sitecore 9 yet, other parts have.

In part one I’m going to talk about identifying your visitors as early on in the visit.

By Identifying users I am referring to allowing users to show up in the Experience Profile.

ExperienceProfileButton

By default, Sitecore will not track every single anonymous user that reaches your site.  In order to get them to show up in the Experience Profile, a few quick changes are necessary.

One of the simplest ways to do this is using either WFFM or the new Forms components in Sitecore 9. In fact, the setup hasn’t changed all that much between the version for this particular use case.

Sitecore 9:  Setup forms save actions

Sitecore 8:  Setup save action in WFFM

You can use these save action with any forms on your website that collect personal details. The identification of a user should be happening when a user submits a form that contains personal details. This is a well documented OOTB forms feature that you can set up without any developer intervention.

Another way to identify a user is to do so programmatically by updating the contacts facet details and then calling the identify method on the tracker.

Sitecore 9:  (reference)

Sitecore.Analytics.Tracker.Current.Session.IdentifyAs("sitecoreextranet", "identifier");

Sitecore 8:

Sitecore.Analytics.Tracker.Current.Session.Identify("identifier")
A side by side code example is available here. 

Calling the above line of code with a string identifier associates the visit data with that identifier. When the user visits again on a different device or tracked website if you are able to call the same line of code with the exact same identifier the visitor’s data will be merged into a single Experience Profile record.

Taking the above concept a little bit further we can also track users across non-Sitecore based websites. By using some google smarts and injecting the FXM beacon onto a third party website we can continue to track the user including, page visits, goals, and outcomes.  (this is covered off more in Part 3)


 

No User is Anonymous

Given that we can choose when a user should be identified and displayed in the Experience Profile. Its time to introduce a concept that no user is anonymous. In fact, this is true for the majority of websites in existence, if they use Google Analytics.

Google assigns an identifier called the Client ID to each visitor that comes along to your website.  The Client ID is stored in the GA cookie and has an expiration date of 2 years after creation.

Note: Google also has a concept of User ID that is used to track sessions across devices. The difference is that each website must send this value to Google in order for it to be used. In reality, this is going to most relevant if you only want to identify users in Sitecore if they have performed Authentication. 

We can use Google’s Client ID to allow the user to show up in the Experience Profile as early as is necessary.

To do this setup the following:

  1. Read the Client ID via JavaScript
    • if (typeof ga !== "undefined"){
          cid = ga.getAll()[0].get("clientId");
      }
  2. Send the Client ID to XDB / XConnect via async javascript.  (Github Reference)
    • if (typeof cid !== "undefined") {	
      	var setEventPath = '/api/xdb/Analytics/TriggerEvent/Event/?eventName=updateGoogleCid&data=' + cid;
              $.ajax({
      		type: 'POST',
      		url: setEventPath,
      		dataType: 'json',
      		success: function (json) {
      		      setCookie(cookieName, cid, 1);
      		},
      		error: function () {
      		      console.warn("An error occurred triggering the event");
      		}
      	});
      }
    • The above code assumes a custom Controller was set up to trigger Goals via Ajax/Javascript.  (Github Reference)
  3. Identify the user (See code examples mentioned earlier or look at our example controller)

Note 1:  The above code only needs to be triggered once per visitor. To save this running multiple times you can assign a cookie to the user. By checking if the cookie has been set you can prevent the above process from running more times then necessary. 

Note 2:  In a single site environment you may choose to leave this identification until a certain amount of visit data has been collected.  For example, writing some logic to check that the user has achieved a certain amount of goals.  This will prevent users with little or no data showing the Experience Profile. 

Note 3: In a multi-site environment the opposite to note 2 becomes necessary. With visitors hitting multiple sites you need to identify them as early as possible. The main reason being that you want any visit data collected from the current website merged with visit data from any other site visits. The resulting merged data provides a great overview of a users movements. This will be discussed more in part 2 when we will look into multi-site XDB visitor identification using a Global common cookie.


Experience Profile – First Name, Last Name

This next step is optional. Given that you have identified the user it will now show in the Experience Profile.  At this point, you may not have a first and last name for that visitor. As an alternative, you could split the Client ID into two numbers and use them as the initial values for the first and last name. If the user completes a newsletter signup, logs in or makes an inquiry at a later time, that would be a good opportunity to update these to the correct values.

firstlast.png

(Github Reference)


 

Part 1: Conclusion

We have demonstrated above how you can identify a user so that they show up in the Experience Profile. As part of this, we have looked at how you could use the Client ID from Google to identify the user as early on as you like, potentially on the very first-page load.  In part 2 of this series on Experience Profile customisations we take a look at how to track users across multiple top level domains.

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 “Sitecore Page Speed: Part 1 : Above The Fold Content”

Sitecore Page Speed: Part 1 : Above The Fold Content

In this series of blogs, I am going to run through ways in which you can increase your score on the Google Page Speed Insights tool.

If you’re not familiar with PageSpeed Insights head over to the page hosted by Google and enter the URL of a Sitecore project you have recently worked on.  It will give you a rank out of 100 and then advise on what is wrong with the way your page HTML, JavaScript and CSS assets are loaded. It will also provide feedback on the way your server is setup and how assets are cached.

If you’re getting a score below 50 on Desktop or Mobile I would suggest it’s time to look at ways you can improve your layouts and renderings.  A score above 80 and you’re really doing pretty well.

If you got a score of 100 … you should probably be writing this blog instead of me.  🙂

Topic 1: Above the fold content / Critical CSS

One of Google’s recommendations is to use a technique called:

  • Above the Fold
  • Critical CSS

As you can imagine this refers to the CSS required to render only the visible part of the page.

In order to do this, you render the critical CSS inline within the head tag. The rest of your CSS is then loaded using a deferred script block approach.  This is all demonstrated in a simple example on this page.

The way this works is the inline minified CSS is delivered over the network as part of the page payload. It is not an external asset and will not block the page load via another network request. The page can, therefore, display the visible section (above the fold) immediately after the page is downloaded.

The bulk of CSS required by the page can be loaded a few moments later via deferred network requests.

What is the best way to construct the minimised block of Inline CSS you ask?

Lucky for us a very handy node tool called “critical” is available for download.

You can really easily spin up a gulp script that will generate all the critical CSS for the main pages across your Sitecore website.

A full example of a gulp critical script can be found here.  Download it and simply run:

  • npm install
  • gulp

This will generate a series of CSS files that contain critical viewport CSS.


In the next blog post, we will cover off how to integrate this inline CSS (above the fold) into our Sitecore layouts.


References:

 

0 comments on “Dynamic Placeholders and Sitecore Powershell”

Dynamic Placeholders and Sitecore Powershell

This post gives a detailed example of a Sitecore PowerShell script that automates the process of inserting content components (renderings) with particular content into a number of items.

Scenario:

Recently a client of ours wanted to roll out a promotion content row to all items of a particular template.

  • These items also happened to be bucket items so there were quite a few of them.
  • The components were usually added to the page via the Experience Editor.
  • The components were used in a building blocks manner:
    • Add a full-width Row
    • Add a Two column component inside the row
    • Add an image in the left-hand column
    • Add a rich text component in the right-hand column
  • The components were added via Dynamic Placeholders.

You could achieve this in certain cases by adding to the Standard Values however if your items already vary drastically from the Standard Values you may not get good enough coverage.

So here is the script with lots of comments so that you can follow along.

View the full script in our GitHub Repo here.

Key Take Aways

  1. We found you need to use Add-Rendering and then Get-Rendering in order to get the correct UniqueID of the rendering just added.
    • In order to construct the Dynamic Placeholder name correctly, you will need the correct UniqueID of the parent just added to the page.
    • The easiest way to facilitate that lookup in Get-Rendering was to give the rendering a unique parameter to identify it.
       (-Parameter @{"InNewPromotionRow"=1;
    • Without this, you may get a list of all renderings that match the non-specific lookup you just used in Get-Rendering
  2. Because of the way Dynamic Placeholders are added to the page this worked best in the Final Layout.
     -FinalLayout
  3. If things go really wrong in your script its best practice to spin up a new Item Version so that you can roll these changes back via another script.
     Add-ItemVersion -Path $i.Paths.Path -IfExist Append -Language "en"
  4. If you have two dynamic placeholders with the same base name then an additional postfix is used to target the second one. So for instance in our case, we had a left and a right-hand column with dynamic placeholders that use the same base placeholder name. In order to target the right-hand side you use “_1” on the end:
     $colWidePlaceholderRight = "/page-body-meeting/row_$rowId/col-wide_$($twoColumnRenderingId)_1"
  5. Nested Dynamic Placeholders: The line of code in point for above also shows how to construct the placeholder key of a nested dynamic placeholder.  In this case, we have a Column inside a Row. The PowerShell script you use to build your components needs to keep track of the placeholder hierarchy so that you can continue to construct nested dynamic placeholders.

 

0 comments on “Getting settings from Sitecore config that aren’t in the settings section”

Getting settings from Sitecore config that aren’t in the settings section

Recently I had to write some code that got the value of the scheduling frequency in Sitecore config.  Normally, to get a value from Sitecore config, I’d simply use:

Sitecore.Configuration.Settings.GetSetting("yoursettingsname")

However, in this case, the value that I want is not in the <settings> node and the above code won’t work.  The value I want is in the <scheduling> node:

 <!-- SCHEDULING -->
 <scheduling>
 <!-- Time between checking for scheduled tasks waiting to execute -->
 <frequency>00:05:00</frequency>

To get around this, instead, I used:

Sitecore.Configuration.Factory.GetConfigNode("scheduling/frequency")?.InnerXml

The above code should work for any Sitecore config item just by specifying the correct Xpath location for the value you require.

0 comments on “Helix Development Settings and GIT”

Helix Development Settings and GIT

When you first start out working with the Helix architecture two files will standout as needing to be unique for each developer. Both of these file are part of the habitat example project that you can find here.

  • gulp-config.js (in the root of the project)
  • z.Website.DevSettings.config (\src\Project\Website\code\App_Config\Include\Project\)

These files contain directory locations that are probably going to be unique depending on how a team member sets up their projects.

The trick is that we want these files in git but we also don’t want the developer to check these files in again with there own settings each time.

The solution:

In the respective folders containing the files run the following git command in the cmd prompt:

git update-index --assume-unchanged

Your probably going to want to document this as one of the required setup steps after checking out the project from you git repository.


What if a developer ever needs to change this file and check it in again. They can reverse the process with:

git update-index --no-assume-unchanged
0 comments on “Using a bucket as a datasource”

Using a bucket as a datasource

download (1)A common scenario requirement these days is to enable content editors to select an item that resides in a Sitecore bucket. The simplest solution is to use a Sitecore multilist with search field.

Another common requirement is to only allow the editor to select one item. In this case we dont have a drop list with search field so again the simplest solution is to use the Sitecore multilist with search field.

First you need to set the source of your field to display items within your bucket of a specific template(s).

Example: StartSearchLocation={11111111-1111-1111-1111-111111111111}&Filter=+_templatename:sample item

Here is an article describing how to set the source of your field: Sitecore 7 field types

If you need to limit the selection to one item for example then you need to also apply some regex. To achieve this you need to enable standards values in the view tab so you can alter the data section.

In the data section add the following regex: ^({[^}]+}\|?){0,1}$ and add some validation text.

Example:

Lqmqh
This article provides additional information:Limit selected items on Sitecore multilist field