In this specific context, this was a Sitecore site and was reproducable by triggering a change to a configuration file, which causes the application to restart. To confirm the shutdown was happening, I could see the regular shutdown messages appearing in the logs:
19060 13:13:41 INFO ************************************************
19060 13:13:41 WARN Sitecore shutting down
19060 13:13:41 WARN Shutdown message: IIS configuration change
HostingEnvironment initiated shutdown
In a typical Sitecore site, this would be followed by the next log file being generated and filled with logs of the initialization process as the site started up again. However, in this particular instance this wasn’t happening. Additionally to this, requests to the site were simply timing out without a response being served.
Before I get into the diagnosis and solution of this problem, it’s worth noting that though this example is based upon a Sitecore ASP.NET site, the diagnosis, issue and required solution are all applicable to a generic ASP.NET site. So if you are not developing a Sitecore site, this blog post could still prove useful to you.
To diagnose this issue, I had to do two things. First of all I needed to take a dump of the process in its “hanging” state, so that I could perform some analysis on it to try and find the issue. This can be done in Task Manager. I triggered the shutdown process of the application, and then I created the dump file:
Then, I opened up Microsoft DebugDiag Analysis. This is a useful tool that can help with analysing crashing / hanging issues in Windows processes. Once downloaded, using this is as simple as pointing it at the dump file you’ve just created, selecting CrashHangAnalysis
as the analyzer you want to use, and starting the analysis:
The analysis will run and generate you a report. This report is an MHT file, an older HTML web archive file format that you’re best opening with Internet Explorer.
Opening the report and looking at the summary immediately showed something promising as a lead to the issue: “The finalizer thread 16 in this w3wp.DMP is blocked”:
Clicking on the thread number (16) showed more details:
From this part of the report, I could determine that the thread was stuck waiting in the dispose method of MsieJavaScriptEngine.JsRt.Edge.ChakraEdgeJsRtJsEngine
. A quick bit of googling turned up this issue on GitHub for the MsieJavaScriptEngine project. It appeared that there was an issue with the version of the package that we had referenced - the site in question was using React.NET which in turn had taken this package as a dependency.
Fortunately in this instance, the resolution was straightforward. The issue had been fixed in a more recent release of the MsieJavaScriptEngine project, so it was just a case of upgrading the package in the solution. This fixed the issue and the site was able to shutdown / recycle as expected.
Nice and simple! Of course, there could be any multitude of reasons why a site may hang or become unresponsive, and it won’t always be so easy to diagnose. Hopefully what worked here may prove useful to you, and the DebugDiag tool could well assist you in the same way it did in this scenario.
]]>You have likely used the following method for reading out a setting from a Sitecore configuration file:
Sitecore.Configuration.Settings.GetSetting("StringSetting", "defaultValue");
It’s worth knowing that you usually don’t need to manually parse values from configuration settings, as there’s a few other methods here that will do that for you:
Sitecore.Configuration.Settings.GetBoolSetting("BoolSetting", true);
Sitecore.Configuration.Settings.GetIntSetting("IntSetting", 5);
Sitecore.Configuration.Settings.GetDoubleSetting("DoubleSetting", 5.00m);
Sitecore.Configuration.Settings.GetLongSetting("LongSetting", 5);
These are just small wrappers around some simple parsing logic, but they’re handy nonetheless. Each variant allows you to specify a correctly-typed default value that will be returned if the setting doesn’t exist, or if it fails to parse.
As well as these, there’s a more interesting option! Have you ever needed to store a time-based setting in configuration? Did you end up storing it as a numeric value and calling your setting TimeoutSeconds
or DelayInMinutes
? Well thanks to the following method, you can have a little more flexibility in values that represent spans of time:
Sitecore.Configuration.Settings.GetTimeSpanSetting("TimeSpanSetting", TimeSpan.FromMinutes(5))
This method makes use of DateUtils.ParseTimeSpan
to parse the string value as a TimeSpan
, allowing for you to have a more varied and readable string. Here are some example values you can use:
d.hh:mm:ss
0.00:00:30 - 30 seconds
0.00:01:30 - 1 minute, 30 seconds
0.10:01:30 - 10 hours, 1 minute, 30 seconds
3.10:01:30 - 3 days, 10 hours, 1 minute, 30 seconds
Very helpful!
There’s also GetFileSetting
, which simply reads the value as a string and feeds the result into FileUtil.MapPath
to map the relative path to an absolute path on the server.
Finally, though there isn’t a GetSetting
wrapper for it, there’s a useful string parsing helper that you can combine with the regular GetSetting
method, and that’s StringUtil.ParseSizeString
. This takes a string representing a file size, and returns the value as a long representing the same size as a number of bytes. Here are some examples:
StringUtil.ParseSizeString("100KB") // = 102400 bytes
StringUtil.ParseSizeString("100MB") // = 104857600 bytes
StringUtil.ParseSizeString("100GB") // = 107374182400 bytes
Like with GetTimeSpanSetting
, this lets you store your configuration values as more flexible and readable strings. After all, it’s much quicker to read 100MB
than 104857600
and know what the resulting file size will be.
Fortunately, this sort of grunt work is just what SPE is perfect for. It didn’t take long to put a script together, and within minutes all of the content was updated. I’m sharing it in this post in case someone else finds it useful.
In our situation, we needed something that would process content items, find renderings that were on placeholder X and then update these to set the placeholder as Y. We also wanted to make sure that renderings on placeholders within those renderings were also catered for (e.g. a rendering on placeholder X/A would translate to Y/A). Further to this, we actually had a number of placeholder “mappings” such as this that we wanted updating all in one go.
This is the script that fixed the content:
|
|
That’s all there is to it. The general flow of the script is:
There are two variables you can tweak in the script: one for determining whether to update the Shared or Final Layout, and the other to run the script in “Report Mode” where it will just find items that require changes, without actually going ahead and making the update.
I hope that proves useful to someone!
]]>A Virtual User within Sitecore is a transient entity used to represent an authenticated user on your site, unlike a regular user that is persisted within the Sitecore Core database (by default!). A common use-case for this scenario would be if you are using an external system for authentication. A Virtual User allows you to create an authenticated session for that user, without needing to create them in Sitecore. They don’t get fully persisted, and you won’t see them in the User Mananger. However, Sitecore treats them like any other user when it comes to roles + access rules. You’re even able to specify custom properties for virtual users.
Logging in a Virtual User is very straightforward. Here is a typical example for creating a new Virtual User and setting some roles + properties:
|
|
After the LoginVirtualUser
is called, an authenticated session is established and on future page requests, the Sitecore.Context.User
will be extranet\bertie@bassett.co.uk
.
A Virtual User is often referred to as an “in-memory” user, suggesting that the user data is held within memory and won’t survive a server restart. This isn’t really true, as we’ll see.
If you are using Forms Authentication, when the LoginVirtualUser
method is called, a standard Forms Authentication cookie is created. By default, this has the name .ASPXAUTH
, and will have an encrypted value such as this:
|
|
During an HTTP request, this data is decrypted into a FormsAuthenticationTicket
. In this demo instance, it contains the following data:
|
|
This ticket alone is enough for Sitecore to consider the user authenticated as the specified user and allow access to any pages you may have set as restricted to anonymous users.
In the above code example, we added the virtual user into the extranet\CustomRole
role and also added some profile properties. As you can see, this information isn’t found within the ticket. So where is it? Well, note that there is a UserData
property which contains a string, cj5qfethsr0z4ej1xjxkh4ox
. That’s important.
If I take a look at the Core
database for this instance, I find the following row in the ClientData
table:
The value of SessionKey
field refers to both the current ASP.NET Session ID and also the UserData
value from the authentication ticket. The Data
field is a Base64 encoding of a serialized Hashtable
object. Within that, is a serialized instance of a Sitecore.SecurityModel.UserRuntimeSetings
object that contains all of the Virtual User data, such as the roles they belong to, and their custom properties.
So as you can see, data is persisted for the Virtual User. Additionally, as the cookie is persisted on the user’s machine, the authenticated user and their data will survive a restart of the web process / server. If you have multiple load-balanced servers referencing the same Core
database, then the Virtual User data will be available to each server, avoiding any load-balancing issues in this particular scenario.
It’s worth noting that this data survives if the ASP.NET Session ID is abandoned. In this instance, the session cookie is removed, but the authentication cookie remains. As this authentication cookie still retains the key within the UserData
property, the data can still be pulled out of the Core
database.
You should note that the ClientData
table does get cleaned up on a regular basis. There is an agent responsible for this, and that executes every 4 hours by default:
|
|
This uses a parameter of the ClientData configuration to determine the object lifetime
, which defaults to 20 minutes. Any rows which have not been accessed within that time in the table will be removed when the agent executes:
|
|
So, without changes to this, you shouldn’t treat this storage of Virtual User data as a permenant. But then, it’s not intended to be - generally this would be storage of data you’re only interested in keeping for the duration of the user’s session. If you need something more than this you should look into an alternate method.
The ClientData table has actually been within the Sitecore database for quite some time now, but the use of it to persist Virtual User information is relatively recent. In fact, it was introduced in Sitecore 8.1 rev. 160302 (8.1 Update-2)
.
So what happened before then? Well, in earlier versions of Sitecore, the user profile data was actually stored entirely within the authentication cookie:
|
|
So, prior to Sitecore 8.1 Update 2, there was no dependency on the Core
database for storing Virtual User profile data. As long as the cookie is in place, the Virtual User would be correctly restored.
That’s what I’ve found from my short dive into Virtual User operations. If you think I’ve stated anything inaccurate, please let me know! I’m going to be looking at this further and into any other common questions around Virtual Users that I see, so if needed I’ll provide a follow-up post with more detail.
]]>First, just to touch on Sitecore 9.0 in general. There are already some great resources available, created by Sitecore and the community to get you up to speed:
Installations in Sitecore 9.0 have become a little more complicated, and there’s a bit more to get used to, especially when it comes to setting up your own development environment. The following blog posts provide some good pointers and scripts to get you going faster:
SOLR is essentially a prerequisite for Sitecore now. If you have only been using Lucene so far, I would strongly recommend you investigate SOLR and move towards it as the norm for your projects. Fortunately, with the introduction of SIF and the new configuration roles, switching over to SOLR is much simpler than in previous versions of Sitecore.
xConnect is a brand new concept for Sitecore 9.0, and as such you’ll want to make sure you’re up to speed on it. Rather than go into detail here on what it is and what you need to know, there are already some really good resources that have been written that I can point you to.
First off, there is the official documentation for xConnect, which is chock-full of examples and guidance, so shouldn’t be overlooked.
Next, my man Jason Wilkerson, aka longhorntaco, has created a fantastic series of blog posts introducing xConnect and also provides sample code for getting your first xConnect client running and pushing interactions into xDb. All the example code is included on GitHub too, nice!
There is currently an outstanding bug relating to tracking interactions from a non-Sitecore xConnect client. When these interactions are added to a contact, they will then cause an error to be shown when the Experience Profile is used to view that contact:
Along with seeing this error, you’ll also have the following error logged:
|
|
The issue occurs due to the interactions from an xConnect client not supplying all of the facets that a typical web visit interaction does, such as User Agent + IP info. The code that executes within the Experience Profile to render visit data expects these facets to be populated.
This issue is already known to Sitecore, and is listed on the Sitecore 9.0 Known Issues page:
The Experience Profile currently only supports website visit interactions. As a result, if an interaction does not fully populate the facets required for a web visit, an error is displayed.
There are however, a couple of workarounds you can put in place to fix this issue. These involve either:
This would be worth a blog post on its own, but fortunately, there is already a great post covering how to implement these workarounds.
Additionally, you may notice that within the Experience Profile, the default search dashboard doesn’t seem to show any results, even if you know there are contacts present:
This is due to another Known Issue in Sitecore:
The To date field must be filled in to see search results. This field is hidden by default and you must select the Filter button to enable it. (187153)
To workaround this for now, you need to open up the filter and make sure the To Date field is set to a value that covers an interaction for your contact:
With this in place, you should see that the contacts start appearing.
Still facing an issue relating to Sitecore 9.0 or xConnect that no one has yet covered? The Sitecore StackExchange is, as ever, the no. 1 resource for questions + answers relating to all things Sitecore. Questions can be tagged, so it’s worth browsing the xConnect questions to see if someone is encountering the same problem as you.
]]>
|
|
Despite the slightly cryptic error message, the cause of this is actually fairly straightforward. If you see this error, this is an indicator that xConnect cannot connect to the Xdb.Collection.ShardMapManager
database, which is attempted in the InitializeShardMapManager
method, where this exception is being thrown.
Though the underlying SQL error doesn’t appear to be logged, to diagnose the problem, you should confirm that the collection
connection string in the configuration is correct:
|
|
In my instance, the Sitecore 9 install hadn’t correctly created the collectionuser
login in SQL Server, so I just had to create this login and add it to the sc90_Xdb.Collection.ShardMapManager
database. I confirmed that the connection string worked and provided access to the ShardMapManager database, and after that point, the error disappeared and I was able to push data into xConnect.
This error occurs if the post-installation manual steps are not completed. For Sitecore 9.0, these are found in Chapter 6 of the installation guide.
]]>And the package generator:
If you have a script featuring a dialog that you regularly execute, you might want to have your most recently used values saved, so that next time you execute the script you can skip through the dialog. Fortunately, this is actually quite easy to accomplish.
There are a number of different locations you could use to persist this data. Assuming you wish to save the values on a per-user basis, one of the easiest methods is to use the ASP.NET User Profile. This is part of the ASP.NET Membership Provider that Sitecore’s user management is built upon. Using the profile means that the data will be stored within SQL Server and therefore persisted across multiple browser sessions and machines. Furthermore, the Sitecore API and SPE makes accessing this profile very simple.
Getting the current user and setting / getting a profile value from it couldn’t be simpler:
|
|
Straightforward, right?
Let’s use the above example and apply it to a script that uses a dialog. In this example, we will show a simple dialog with a dropdown list. When the dialog is completed, the value that was selected will be stored in the current user’s profile, ready for next time:
|
|
If you execute the script, you will see that once you have completed the dialog once, the value you last selected is preselected for you the next time you run the script. Easy, right?
]]>One of the great benefits of Sitecore is that it allows you 100% control over the HTML output of your site - it doesn’t inject unwanted scripts or styles, and it doesn’t force you to conform to a certain HTML structure. However, with great power, comes great responsibility.
I am sure you have worked on a site that has been lovingly crafted by your UX team, beautifully imagined by designers, and expertly rendered by front-end developers, only for the site to then be gifted to content editors. At this point, using Sitecore’s standard rich-text editor, they add their own unique touch by way of tables for layouts, overriding fonts, and center-aligning text to their heart’s delight. The result is not quite the dream your team first had, or indeed, sold to the client.
Often, this is not the fault of the editors; content gets copy + pasted from Word (or other applications) and customized HTML comes along for the ride. And all those lovely styling buttons are right there in the editor, why not use them?
Yes, there are means of tackling this in Sitecore. You can limit the functionality of the rich-text editor by removing most of the options, but Sitecore’s editor can still result in some undesirable HTML even with very few options available to the user. And yes, there are buttons there to assist in the copying of text from Word, if your editors remember to use them.
You can even go down the road of validating the HTML created and show the user an error if their HTML isn’t up to scratch. But if you’re going to that trouble, why not present a solution rather than a problem and silently tidy up the content for the user immediately? That’s what this small module, Sweep, sets out to do.
Despite the dramatic introduction, Sweep is actually a very straight-forward and simple module. Upon the saving of an item through the UI - i.e. in Experience Editor or Content Editor - Sweep will pass the fields being saved through a pipeline that will a) determine if they need to be cleaned and b) clean them if required.
Determining if they need to be cleaned is down to the configuration of the module. You can configure Sweep to clean all Rich-Text fields if you desire, which is not something I have used myself, or you can take a more granular approach and only apply it to certain templates and fields.
The cleaning is performed by an extensible pipeline which can do as little or as much as you like. Included in the module are options for:
<p style="margin-top:430px">A lovely paragraph</p>
–> <p>A lovely paragraph</p>
)<p></p>
)<p><strong>My title!</strong></p>
–> <h2>My title!</h2>
)<p><p>My text</p></p>
–> <p>My text</p>
), yes this can happen!<h1><strong>My strong header!</strong></h1>
–> <h1>My strong header!</h1>
)These are just a collection of examples that I have used in various projects. It is by no means suggested that a site should need to use all of these options, or that all of these make for Perfect HTML™. Every situation is different, and these are just some of the provided options that can be used. It is also super-easy to add your own by extending a provided processor base class.
This is definitely not a module for all sites, or even most sites, but I have certainly made use of it so I have added it to GitHub in case others can use it too.
The module is hosted on GitHub. It has also been submitted to the Sitecore Marketplace and will hopefully be published shortly.
I would be very keen to hear from anyone who has tried the module and wants to provide feedback. Or if you’ve read this blog post and have any questions / comments, please comment below or message me on Sitecore Slack for a chat about it.
I’d especially like to hear from you if you think it’s either fundamentally flawed or can be achieved in a much better manner!
]]>Sitecore includes a Developer security role that can be assigned to users. This role includes both the Author and Designer roles, as well as adding further permissions. The full description supplied by Sitecore is:
Gives the user access to content manipulation facilities in the Content Editor, plus all the design and authoring roles normally used by client authors and client designers. It also provides access to more functionality on the ribbon of the Content Editor to allow full development features for users assigned to this role.
This role also has access to the Development Tools menu in the Sitecore menu, which gives the user access to further development tools, such as the Package Designer.
For a full description of the security roles within Sitecore, see this documentation page.
However, if you assign this role to a user then there is an issue that the user may encounter when trying to use some Developer functionality. If they attempt to insert an item in the Content Editor using the Insert From Template
option, they will see the following exception:
|
|
This error stems from the fact that the Author role has been explicitly denied access to the Template Lister
application, but not to the Insert From Template
button itself. They can see and click the button, but when the application is launched, it throws the error.
This has been reported to Sitecore and confirmed as a bug, and is being tracked under the public reference 113200.
As a workaround to this issue, you can do the following:
sitecore\Author
from the sitecore\Developer
rolesitecore\Author
inherits directly to the sitecore\Developer
role.This means the Developer gets the roles that Author has, but not the DENY permissions that have been recently set explicitly on the Author role.
Alternatively, as stated above, these same DENY permissions for the Author role don’t seem to be present in Sitecore 8.2, so upgrading to that version will restore the correct functionality.
]]>To tidy this up, I prepared an SPE script that would find items that are only used by a single page and then move them to sit under that item, inside a Content folder.
This is the script:
|
|
That’s all there is. The general flow of the script is:
The top-half of the script can be executed (uncomment the return line) if you just want to identify which items can and can’t be moved.
]]>Below is an example of such a dataset, where we have a bulk set of changes to be made to the MetaTitle + MetaDescription fields. In this example, the item reference is an item path, but this could just as easily be an item ID.
|
|
To prepare this data for the import, we want it in CSV format. If the data is within Excel, Excel allows you to Save As
the spreadsheet to convert it to a CSV. Make sure it to include a header row in the file, as seen in the example above.
Then, within Sitecore Powershell we can use a very simple script to import the data:
|
|
And that’s all there is to it! The magic is in the Import-CSV
command, which is part of regular Powershell, and imports all of the data into the $importData
variable. The script can then iterate over this data and read each row to find the item and then make the updates. As long as the CSV file hash headers, each $row
variable will have properties exposed that correspond to each of the columns, e.g. $row.ItemPath
.
The -ErrorAction SilentlyContinue
is there to swallow the exception that is raised if Get-Item
can’t find the specified item. To handle this in a more user-friendly manner, we then perform a null-check on $item
and if that fails we log out the ItemPath that could not be found.
Pretty simple, huh?
For an additional performance boost when importing your data, you can optionally wrap the import in an instantiation of the Sitecore.Data.BulkUpdateContext
class, which prevents indexing + events from firing after each individual update:
|
|
I had a great time, and along with seeing 3 other great presentations, I was super pleased by the positive feedback I received on my talk. Thank you to everyone who attended!
There was a video taken on the night, so if you couldn’t make the talk and want to watch it, it’s just below. Unfortunately there was an issue with the recording and some audio was lost, but I’ve supplied a voiceover for that part to cover what was missed:
If you’re interested, I have uploaded some of the materials from the talk:
Bear in mind that if you use the packages, you’ll need to have SPE installed first. See just below for links!
Here are the links to the good stuff:
One of the questions I was asked a couple of times after the talk was whether you are able to debug scripts in Sitecore Powershell Extensions. I was using version 3.3 in the demo, which did not have this feature, but the very newly released version 4.0 comes with full debugging support!
Here’s a video from Adam Najmanowicz demonstrating the new debugging functionality:
Pretty great, huh?
]]>These two bugs are:
Here is a demo illustrating both issues:
This bug is down to the FieldChromeType.js
file used in the front-end to provide the editing functionality for the fields. It is incorrectly adding in two <br>
tags into the HTML when a new-line is created.
This issue has been reported to Sitecore Support and has been confirmed as a bug. If you have need to reference this bug in a support ticket with Sitecore, the public reference number is 103584.
Sitecore Support have supplied an updated version of the JS file that fixes the issue. You can get this file from Support if you reference the above number, also if you log this issue it will help inform Sitecore of how wide-affecting the issue is.
I have also hosted the file here on this site for your convenience. By using this file you accept responsibility for doing so, and I strongly recommend you compare this file to the one you are replacing in your solution to confirm what you are changing, as well as still reporting to Sitecore Support that you encountered the bug.
To apply the fix, just place the updated file in \sitecore\shell\Applications\Page Modes\ChromeTypes
, replacing the existing file.
Note that this fix is for 8.1 Update 1 and Update 2 only.
In Sitecore 8.1 Update 1, the Sitecore.ExperienceEditor.WebUtilty
class was updated to amend the regex that is used to process fields before they are saved. Unfortunately, the new regex in place is too hungry and matches across all <br>
tags, removing them along with all the content inbetween. The result is that only the first line remains.
This issue has been reported to Sitecore Support and has been confirmed as a bug. If you are looking for information on this bug, the public reference number is 101295. This problem also affects the recently released Update 2.
Unfortuntely, the WebUtility
class is static and as such it’s not trivial to implement a replacement for this class. There are no references to it in configuration allowing it to be swapped out with an alternative.
The workaround suggestion by Support at the moment is to use a Rich-Text field.
Alternatively, and the route we have gone, is to remove the ability to edit this field in the Experience Editor and to have our users make the update to this field in the Content Editor. As this was just a single field that is only used on a handful of items, that worked well enough for us. However, if you make extensive use of the Multi-Line field and Experience Editor, you may need to investigate a more robust workaround. If you have done this, please comment below!
Kayee has written a blog post on a very similar bug that also effects the Multi-Line field in the Experience Editor, though in that scenario it’s if you are using it combined with a Custom Experience Button.
Fortunately, that bug has fix available for it. You can see a demo of the bug and get the fix from the link below.
http://www.kayee.nl/2016/01/10/sitecore-8-1-multi-line-text-field-with-experience-button-problem/
]]>/System/Settings/Buckets/Facets
. I created a new item here, called “Friendly Date Range”, that I would use in place of “Date Range”. I associated this with a field I would create, to be called friendlydaterange
.Now, the interesting thing about the existing daterange
field is that it is not tied to an existing field in the index, it uses a Virtual Field. You can see how daterange
is defined in the default index configuration here:
|
|
I had a good look around this class to determine how these fields are created, and thought I would summarise the interfaces that you will need to implement if you want to implement your own virtual field.
A virtual field is a field in the search index that doesn’t actually exist in the physical index. Instead, it is computed at search run-time, and will typically use one or more backing fields that do exist within the index to create an entirely new field. Note that this is different to ComputedIndexField
s which are computed when creating the index and are stored in the index.
Why is a virtual field useful? Well it allows you to have a value of the field is dependent on when the search is run, rather than when the index was made; this is great for field values that are date and time dependent.
In the case of daterange
- the actual index stores the absolute creation date of an item under __smallcreateddate
. This value is set and doesn’t change. However, the daterange
virtual field is used to turn this absolute value of, say, 20150803 into a relative one of thismonth. This new value obviously cannot be stored directly in the index itself, as it will quickly become out of date!
The virtual field works by implementing several methods that are called during the execution of the search. These are used to read and alter the fields of a document that come back with a search, as well as altering the outgoing query when a search needs to be made using the virtual field. There is also full support for faceting on virtual fields.
Let’s look at how the interfaces are implemented. The DateRangeFieldProcessor
shown above implements two interfaces, IFieldQueryTranslator
and IVirtualFieldProcessor
. Here is what each implementation of these is required to do for a virtual field to work. The following was applied to Sitecore 7.2, but should be good for versions following that too.
The IFieldQueryTranslator
has two methods and a property. Here’s what they do:
|
|
Simply the field name your virtual field will use.
|
|
Takes the query from the search, which will include your virtual field being queried on, e.g. daterange:thismonth
. Here you convert that query into a query on the real backing field(s), e.g. A range query on __smallcreateddate
for values that fall in this current month.
This is only called if the fieldName passed in, matches the FieldName property, so it will only get called if your virtual field has been searched on.
|
|
When the results of the query come back from the index, you will want to inject the value for your virtual field into the document. Typically you would inspect the real backing field(s), determine what the value of your virtual field would be, and add it to the fields dictionary to return.
e.g. The document contains a value that falls within the current month in __smallcreateddate
, so “thismonth” is added to daterange
in the fields dictionary.
The IVirtualFieldProcessor
has two methods to add faceting support. Here’s what they do:
|
|
When a faceted search is made, you can inject a FacetQuery
into the search for the real backing field(s) your virtual field uses. This is so that when the results come back from the index, you’ll have that facet data to use to create the facet data for your virtual field.
|
|
When the results come back from the index, you’ll be given a collection of all of the facets and their values. At this point you can take the facet results for the backing field(s) you are using, and process them to create your own facet results.
For example, if with TranslateFacetQuery
you added a FacetQuery
for __smallcreateddate
, you can iterate through this facet result and sum up all of the values that fall within the current month to create your own facet result for “thismonth”.
The virtual facet result is added to the collection, and the backing field results are removed as they are no longer needed.
That’s all you need to do to implement a virtual field. Please leave a comment if there is something I’ve missed or if you have any questions.
]]>The site is pretty basic at the moment, and just hosted through Github for now. But the redesign has brought about a couple of changes:
The syntax highlighting is based on HighlightJS, which does appear to have a couple of bugs when it comes to rendering C# source. Hopefully I can get those ironed out.
I also might look to expand the template and build on more areas in the site, but for now it’s just remaining a blog!
]]>In certain cases, you may want to implement your own custom backing store for users, rather than the default SQL Server provider. Fortunately, Sitecore includes a mechanism so that it can support multiple providers under one website, this is through the SwitchingProviders
within the Sitecore.Security
namespace.
There are already a number of useful links covering implementing your own custom membership provider using the Switcher:
This is just a short blog post to highlight a couple of a small gotcha that you may encounter, due to some inconsistencies in Sitecore’s implementation of the Switching providers.
The first link in the above list is the official Sitecore documentation for implementing new providers. In 3.1.1 Configuring Switching Providers, the following attributes for defining providers via a SwitchingProvider are defined:
Whilst these are the properties for the SwitchingMembershipProvider
, the remaining providers have a couple of additional undocumented options, that have a large affect on how they operate:
The Role Provider adds an additional 2 undocumented properties:
ignoredUserDomains - If this is specified, then the provider won’t apply to the domains listed. Multiple domains should be comma-separated.
allowedUserDomains - If this is specified, then the provider will only apply to the domains listed. Multiple domains should be comma-separated.
As you can see, the way these two operate are at odds with each other (one is a blacklist, the other a whitelist). The provider only expects there to be one provided,; if you provide both, it will throw an exception.
Note that for this provider, the domain
property is completely ignored. This can cause some real confusion if you have implemented a new provider and you are wondering why it is getting called for other domains, even though you’ve specified a domain there.
The Profile Provider adds just the one additional property:
ignoredDomains - This works exactly the same as ignoredUserDomains for the Role Provider.
And that’s it. No option for a whitelist only, and again, the domain property is ignored.
It’s not clear why the Role + Profile providers don’t just provide the same properties as each other, nor why these aren’t available to the Membership Provider. This might just be a small gotcha, but it’s worth taking note of this little eccentricity of the code.
]]>The rest of this post assumes that you are familiar enough with GA to know about the ga() function and how it can be used to send events.
Note: Google Analytics doesn’t require jQuery, but the examples in this blog do use jQuery for setting up events. It is also assumed you are using the new version of Google Analytics, analytics.js.
You’ll need the Google Analytics script defined in your page. If you haven’t done this yet, Google have got you covered.
If using ASP.NET MVC, it’s a good idea to have this in its own view that you can cache the output of. You can also use a configuration setting to determine whether to output the script at all, to avoid enabling Google Analytics on development / test servers, or perhaps to use a different ID in those environments.
Simply adding this script will get page views tracking in Google Analytics.
Once you have the default script, you can then add your own javascript to implement additional GA functionality throughout your site.
The following is a skeleton script that you can then expand on:
|
|
Typically, once this script has been loaded, then, at a suitable point in your site’s code (e.g. after page load), you can call mysite.analytics.init();
to execute the code and setup your events.
If the Google Analytics script hasn’t been added to the page, this script checks to see if there is a ga_debug
variable defined. If there is and its value is truthy, the script creates a dummy ga() function that simply logs to the console. This is useful for testing your analytics events without needing a real GA account in place. If the debug value isn’t in place, this script simply doesn’t go any further.
Right, on to event tracking! As events are simply tracked by calling a function in javascript, they can be associated with almost any user behaviour on a website - this includes clicking on anything on the page, timings between actions, the scrolling of a page, and much more. If you can imagine it, you can likely write it.
Let’s look at some of the events you could create.
First of all, a good piece of advice is to keep your event definitions generic where you can.
Rather than tracking every interaction individually on your site, try to logically group the different types of interaction together and then come up with a method of selecting these elements. This is especially important where you are building a CMS-driven site and the HTML will be generated on the server.
As an example of a poor way of implementing tracking links, look at the following HTML and Javascript:
HTML
Javascript
Whilst this will track both of the links, it is time consuming to implement, and you’re going to need to expand your analytics script every time you add a new link to the site.
How about this instead?
HTML
Javascript
If you prefer, you can use a class selector
instead, though using data- attributes helps to keep the classes of your elements simply for styling. You can even drop this method and go for any* click on a link using the a
selector, though this is rather indiscriminate. It’s up to you ultimately on what you’re interested in tracking.
The message here though is to try to group the interactions where you can, so your tracking code can be simpler. There are, of course, times where you will want to track specific events that only occur in single places on your site, and that’s ok.
Now let’s look at some examples of events you can track.
Use this to track when a user clicks on an email link.
HTML
Javascript
Tracked result
Category | Action | Label |
---|---|---|
Link Clicked | mail@server.com |
If you have social media “Share” links that you executing as part of JS, you could also track them through GA events.
HTML
Javascript
Tracked result
Category | Action | Label |
---|---|---|
Share | Link Clicked |
Similar to the example above, a generic link tracking. This however also includes the text of the link, which could prove useful in determining which buttons on your site are resulting in the most clicks.
HTML
Javascript
Tracked result
Category | Action | Label |
---|---|---|
Button | Clicked | View products -> /products |
This is useful if you have some top-level header navigation that contains links, and you want to track the level of user interaction with these specific links.
If you have a footer, you can apply the same principle but using a separate data element, for example ga-footer-links
.
HTML
Javascript
Tracked result
Category | Action | Label |
---|---|---|
Header | Link Clicked | Products -> /products |
You don’t need to restrict your interaction tracking to just page links on the site. You may already have events setup on your site for particular types of functionality, for example a “Show More” button that reveals some hidden content on the page:
HTML
Javascript
You may be interested in tracking how many users are actually revealing that content. If you want to do this, don’t edit the previously defined event code that implements the reveal; it’s perfectly fine to have multiple events trigger when a button is clicked, and it is much neater to keep your analytics tracking code together in one place. Instead, you can just add another event:
Javascript
The tiny inefficiency here of multiple events is worth it to keep this code much more maintainable.
Tracked result
Category | Action | Label |
---|---|---|
Show More | Clicked | #my-hidden-content |
A simple page-load isn’t really enough to determine how much of your website a user is actually seeing when they visit. For example, are they just viewing the page and immediately leaving, or are they actually scrolling down to see more content? How many of your users do this?
To help capture this sort of information, you can track when and how far a user scrolls on a page. Whilst you can write this code yourself, there is already a small JS library that wraps up the functionality for you - Scroll Depth.
By default, Scroll Depth tracks events for the percentage a user scrolls down the page - firing events at 25%, 50%, 75% and 100%. However, as well as customizing these percentages, you can also specify the IDs of elements on your page and it will fire tracking events when a user scrolls far enough down for that element to become visible. That’s great, as it tells you how many users are actually seeing a specific part of the page you have designed.
Once the script has been included, a typical way to invoke this library would be:
|
|
Using the above skeleton script as an example, the ideal place to execute this would be straight after _setTrackingEvents
is called.
This has been just a few simple examples of event tracking that you can implement on your site. With the flexibility of Javascript, you can create way more sophisticated events than those mentioned here. Hopefully the above provides a useful primer for you if you are just getting started with Google Analytics.
]]>Each time you create a new Rendering, you need to update the placeholder settings too so that the editors can use the rendering. If you keep your renderings nicely organised within a folder structure, then there’s a simple method for making this a bit more flexible and having Sitecore automatically pick up the renderings by referencing a folder of renderings rather than the rendering itself.
Within the Content Editor for the settings, Sitecore allows you to add a rendering folder, rather than a rendering itself:
Typically, selecting the folder would have no effect. However, with the following pipeline, we can alter the functionality of Sitecore so that if a folder has been added, it pulls in all of the child renderings that sit underneath it.
This can be very useful if you keep your renderings organised by functionality, so for example you want a “Forms” placeholders setting that will contain all of the Form renderings you have created. If you keep them together in the same folder, you can have them added automatically, without having to update the settings.
The following is all the code we need for the pipeline step:
|
|
The code for this is relatively straight-forward. It makes use of a standard DerivesFromTemplate
extension method that you’ll need to implement. There is also a static class used for ID references, but you could just drop the IDs straight in.
The GetChildRenderings method is actually extremely simple and could be removed, but it’s left in to illustrate that you could replace this with other logic, such as all descendants of an item, or perhaps using a parameter to determine what renderings to pick up.
With this code in our solution, we need to add the command to config so that it can be referenced. This can done with a simple config file in the Include folder, with the appropriate assembly + class reference:
|
|
And that’s it! You can now just reference folders in your Placeholder Settings and all of the child renderings underneath will be pulled in.
]]>Have you noticed how the Marketing Center icon in the Sitecore Start Menu is just used to load a new Content Editor window, but with a particular root item? In that particular case it goes straight to /sitecore/system/Marketing Center
.
It’s quite a simple addition but can speed up work for your editors if they’re able to use an icon to launch straight into an area they regularly work with, especially if it’s tucked away in your content tree. Additionally, if you’ve removed access for them to one of the ancestor items, they won’t see it in the standard Content Editor tree at all, even if they have access to that particular descendant.
This short tutorial shows you how to create a new shortcut to launch a Content Editor with a customized root item of your choice.
First of all, you’ll need to create a page that will get shown when the window opens. I have based the code for this Layout on the very same code used by the Marketing Center, no point in reinventing the wheel. All the code really does is launch the real Content Editor ASPX page, complete with some URL parameters to have it open the right item.
Create an ASPX page and fill it with the following, though replace the ID passed to Client.ContentDatabase.GetItem
with the ID of the root item you want to be displayed when the window opens. You can also customize the he
and ic
parameters to give the new window a custom URL + icon.
|
|
Save this ASPX file where you like, you may prefer to go somewhere within the /sitecore
folder, or perhaps the /sitecore modules
folder instead to keep it separate.
Next, create a new Layout item in the Core database. I would recommend following Sitecore’s pattern and keeping it under the Applications folder located at /sitecore/layout/Layouts/Applications
, or perhaps use a subfolder with your project name. The Path
field of this layout should be a relative path to your new ASPX page.
Now you need to create an Application that uses this layout. Create a new Application item under /sitecore/content/Applications
, if you right-click on the Applications item you should find that an Insert Option has already been defined for adding an Application.
In this new item, set the Chrome
field to BorderlessChrome
, otherwise the window won’t display correctly. The Icon
field will control how the Application will appear in the Start Menu later, use a 32x32 icon. You can also set the Display Name
and Tool Tip
fields.
Next, edit the Presentation Details of the item and set the layout to your newly created layout from the previous setup.
Finally, you need to create the shortcut. Under /sitecore/content/Documents and settings/All users/Start menu
are several items that represent the different areas of the Start Menu. Choose the one where you would like your icon to appear and create a new item of type /sitecore/templates/Sitecore Client/Applications/Application shortcut
(you should find it as an Insert Option).
In the shortcut item, set the Application
link field to point to your Application item. If you want a particular shortcut to override the default settings for an Application, you can also set the Icon
, Display Name
and Tool tip
fields.
That’s all there is to it, your new shortcut should be appearing in the Start Menu, and when you click it you’ll get your new customized Content Editor.
]]>Whilst a lot of content editors using Sitecore prefer to use the Page Editor, the Content Editor still sees a lot of use. One of the features of the content editor is the gutter validators, used to quickly provide a visual check of the validation state of items in the tree:
It’s very easy to add your own custom validation here.
First of all, create the code for your gutter validator. Create a new class and have it inherit from Sitecore.Shell.Applications.ContentEditor.Gutters.GutterRenderer
. In this class you should override the GetIconDescriptor(Item item)
method, which returns a GutterIconDescriptor
.
This overriden method should return an instance of GutterIconDescriptor
if the validation has failed, or null if the validation passes.
For the sake of example, here we have a validator that checks to see if an item has over 100 child items and if so will fail validation:
|
|
You can use the above code as a template, all you need to do is customize the ItemIsValid
method with your own validation logic, and alter the GetGutterDescriptor
method to choose an icon and tooltip that’s suitable for you.
That’s all there is too it for the code. Compile the assembly and make sure it is deployed to your Sitecore bin directory.
Now you need to create an item for the gutter validator in Sitecore, so that it can be selected. This should be located in the Core database underneath the item /sitecore/content/Applications/Content Editor/Gutters
.
You should create your item using the /sitecore/templates/Sitecore Client/Content editor/Gutter Renderer
template.
The Header
field represents how your validator will appear in the context-menu, and the Type
field is the fully-qualified name of your new type:
Once that’s saved you can jump back into the master database and enable it. Right-click in the gutter to enable your validator, and then you should see your validation icon wherever an item fails your validation:
]]>