April 12, 2019

Limit TreelistEx Selections to X Number of Items

April 12, 2019

Sitecore Item Limit

I recently had a requirement to limit the content author's ability to select items.  In this case, they were only allowed to select two items.  I love using "TreeListEx" and therefore selected that field type.  I knew that I could set validation on fields and went about looking for the built-in validator that would limit the number of selections. . . I didn't find it.

While I was surprised to not find this validator, I thought that surely, someone else smarter than me had mentioned it on the interwebs; I was not disappointed.  A quick search yielded a StackOverflow post about it that cited a blog post.  In summary, apply a Regex value to the "Validation" field of your field. (Add a helpful "ValidationText" message while you're at it.)

I dutifully copied and pasted the Regex expression into my field and was a little surprised to see an error message while testing the validator:

Sitecore Validation Error

Well, that didn't go as expected.  After a bit of Internet sleuthing on the error, I found that there might be a character (or "characters") in the string that needs to be escaped.  I simply changed "^({[^}]+}|?){0,2}$" to "^(\{[^}]+\}|\?){0,2}$" (included the escaping 'backslashes' before the curly brackets and question mark) and was on my way!

Sitecore Validation Working

Update: And of course as I'm finalizing the research for this post, I see that Ben *actually* did solve this and it can be seen in the screenshot he included, but the Regex text in his post didn't include the escape characters

November 27, 2018

Sitecore CD Hardening via Release Deployments

November 27, 2018

Sitecore CD Hardening via Release Deployments

Security hardening on your CD servers is an absolute must.  It should not be an afterthought and you should address it early so that you're not trying to cram it in just before go-live.

In the days before automated deployments, CD hardening would involve manually manipulating the IIS folder level permissions on each CD server in your environments. With automated deployments, we can make this much easier!

Which Sitecore Version are We Deploying?

Take note of which version of Sitecore you are working with.  Prior to Sitecore 8.2, Update 3, you are still limited to manipulating IIS folder level permissions.  You can also handle this with configuration, but that's outside the scope of this post.  With Sitecore 8.2, Update 3 and newer, we have the option of changing the forms authentication mode in order to secure the server.

Disable Forms Authentication

The simple way to disable access to your Sitecore interface is to change the authentication mode from "Forms" to "None" in your web.config:

Easy enough to do on each of your CD servers, right?  But we want to automate that with our deployments.

Azure Devops and Web.config Tranforms

Using a web.config transform, we can target the "authentication" node and change the value to "None." 

What about local development though? 

When developing locally, you can use the "Debug" solution configuration that lets the web.config use the "Forms" setting when developing locally. This is exactly how Sitecore ships out of the box.

Use the "Release" solution configuration to set a transform on that element to change it to a token value.  Note: This assumes you are using the "Release" configuration when building your solution in Azure Devops (was VSTS). 

Why a token value instead of just setting it to "None"? 

We are utilizing (mostly) clean deploys.  When deploying, we wipe the web root and install all of Sitecore and our custom code and configuration from scratch.  Additionally, we deploy the same set of files to every server.  Different values for the tokens are controlled via variables and variable groups in Azure Devops. We expect this token value to be replaced at the time the release is generated and deployed to our different environments.

Putting it Together

So, we have a Web.Release.config file in our solution that looks like this:

In Azure Devops, we have a Variable Group for our CM servers that has the same "#{AuthenticationMode}" token with a value of "Forms":

Sitecore CM Server Role Variables

Additionally, in Azure Devops, we have another Variable Group for our CD servers that has the "#{AuthenticationMode}" token with a value of "None":

Sitecore CD Server Role Variables

When the release is built, use the Replace Tokens task in your release definition to process all your config files and replace the tokens with values from your Azure Deveops Variables Library.


Security hardening is an important task that shouldn't be overlooked and shouldn't be pushed to the end of your development cycle.  Consider security early and keep yourself and your clients out of the news.

Additional Reading

Sitecore - Deny anonymous users access to a folder

Sitecore - Restrict access to the client

Visual Studio Marketplace - Replace Tokens Task

January 31, 2018

Craig Taylor Wins Sitecore "Most Valuable Professional" Award

January 31, 2018

Craig Taylor wins Sitecore Most Valuable Professional Award

Atlanta, GA — January, 31st, 2018 — Craig Taylor has been named a "Most Valuable Professional (MVP)" in the Technology category by Sitecore®, the global leader in experience management software. Craig was one of only 208 Technology MVPs worldwide to be named a Sitecore MVP this year.

Now it 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.

I'm truly honored that Sitecore recognized me as an MVP in the Sitecore community for the fifth straight year! I'm surrounded by some amazingly talented co-workers at Avanade and supported by an amazing Sitecore community. I feel like I'm in great company and am continually pressed to keep the high standards demonstrated by the other Sitecore MVPs. (Craig Taylor, Sitecore Architect, Avanade Inc.)

"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.

Craig is a five time Sitecore Technology MVP and is a skilled, senior IT professional with over sixteen years of professional experience in Web technologies. He is an expert in architecting and leading teams in delivering Sitecore CMS solutions. Craig has a consistent record of delivering successful software implementations that are on-time and on-budget.

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

December 1, 2017

Errthing is Duplicated! (in the Sitecore content tree)

December 1, 2017

Errthing is Duplicated! (in the Sitecore content tree)

So many Sitecores!  I recently ran into something that I've never seen before.  After performing a build to my local server, I noticed that I was having trouble syncing portions of my content tree via TDS and Rocks.  After further investigation, I noted that every node was duplicated:

You get a content, you get a content, you get a content

No bueno.  The duplicated items had identical guids, so it's not like they were separate items.  This screenshot is from Rocks, but logging into Sitecore showed me the same thing. (8.2 Update 4, btw, but this probably applies to all versions of Sitecore)

I tried just about everything I could think of to resolve this.  No dice.  Off to the trusty Sitecore Slack channels for help!

Side note: The Sitecore community is awesome!  You've instantly got an army of Sitecore enthusiasts willing to help you through any roadblocks you may face.  At times it seems like there are so many places to keep track of, but when you need them, you're glad they're there.  How to connect with the community: https://sitecore.stackexchange.com/questions/1689/how-can-i-connect-with-the-sitecore-community

As I initially thought this might have something to do with TDS, I tried that channel first.  Using the suggestions by others on the channel I tried numerous IIS restarts, Visual Studio reboots, disabled the TDS item cache, removed TDS file caches and rebooted my VM.  None of these helped my situation.  I was starting to consider "nuclear" options where I would attach vanilla master database files and briefly considered "transferring items from web to master."  I was not looking forward to these options and am glad I didn't get to that point.

The Fix

Fellow Sitecore MVP Kamruz Jaman (@JammyKam) thought this sounded like a config issue.  Perhaps there were some config files were corrupt or duplicated, he thought.  He suggested disabling all custom configs to rule those out.  I wasn't sure what would have changed from earlier in the day when everything was working and to a bit later that evening, but I decided to delete all custom configs knowing that I could always just redeploy them.

Well, I deleted the custom config files and fired Sitecore back up (after an IISreset for good measure)  to check and sure enough, the duplication issue was resolved!

I still didn't have my application configs now, so I followed up with re-deploying my application.  Still worked, whew!

So What Busted Sitecore?

I actually haven't had a chance to get to the bottom of *exactly* which config broke Sitecore so hard.  I think there must have been a config that somehow got duplicated (with a different name) or some cruft left over from a bad deploy that caused it to get wonky.  It was definitely not a TDS problem though, just to be clear! ;) I'm just glad the solution was relatively easy.

Sitecore 9 to the Rescue!

Well, 9 isn't the solution to my duplication problem, but it would have been much, much easier if I were already on 9 and could disable an entire configuration layer via the "mode=Off" setting as our hero of this story, Kamruz documents here:

June 15, 2017

Suppress Cookies for EU Cookie Compliance

June 15, 2017

Suppress Cookies for EU Cookie Compliance

You've probably seen this already on a number of sites, but increasingly, sites are being required to inform their visitors of the site's cookie policies.  Many implementations that I've seen provide the disclaimer that cookies are being used and that by interacting with the site, the user is providing consent.  My client is going a step further and requires that *no* cookies (except the ASP.NET session cookie) be written to the browser before the user provides consent.

In the case of this client's site, this includes Sitecore's analytics tracking cookie, Sitecore's language cookie (although technically okay to emit because it's only a session cookie) and a host of other tracking cookies emitted from different renderings.

In order to approach this in a way that prevents the need to edit logic across the solution, we came up with a solution that has two primary components:

  1. A Cookie Consent Provider - This provider allows us to interface with a third party system for determining if consent is required, not required or has already been provided.  Additionally, by implementing a provider, we can easily switch out to another provider at some point in the future and not have to re-write logic.  
  2. An MVC filter - By creating an MVC filter that checks for consent, we can simply decorate methods in our controllers to control the emitting of code/cookies without having to affect any logic already contained in those methods.
I'll talk a bit about the primary components and then describe how they all work together to actually suppress the cookies.

Cookie Consent Provider

The provider is based on the standard Microsoft provider model.  I create a base class that all providers will inherit from.  In the base class, I have methods to get and set the user's consent to use cookies.

I won't review the details of my provider implementation but know that it's pretty vanilla with a provider that implements the base class along with the necessary configuration elements in code and the web.config.

MVC Filter

I chose to use an "AuthorizeAttribute" filter to allow me to suppress the execution of controller methods.  The authorize attribute returns a simple "true" or "false" value indicating if the calling method has the required permissions to execute.

The two primary areas to concern ourselves with in the filter are the "AuthorizeCore" and "HandleUnauthorizedRequest" methods.

In "AuthorizeCore", we are checking the need for consent.  It returns a bool value indicating if the current request can be executed.  Also note a bit of tricky logic there regarding the "InvertLogic" parameter.  Basically, I have times when I need to execute the calling method when the result of the "AuthorizeCore" check is false.  By passing in this parameter, I can invert the logic to return the desired results.  In my case, I want to execute a controller rendering that outputs my cookie consent banner when consent is required.  I can just decorate my method with the additional parameter:

In "HandleUnauthorizedRequest" (the code that is executed when "AuthorizeCore" returns "false") we are setting an AuthorizationContext Result.  In my case, we wanted to output an HTML comment that indicates the calling method along with the status so that we could easily confirm that the system is functioning normally when we view the source of the page.

Putting it Together 

As mentioned, the goal here is to suppress cookies from being emitted before the user has given consent.  The two Sitecore-specific cookies that I am blocking are the 'analytics' and 'lang' cookies.  I block these by inserting pipeline processors that check my consent provider.

Note: Always use patch include files and refrain from editing the Sitecore.config file directly.  

It's also worth noting that I have a configurable 'AnalyticsCookiePrefix' parameter that I pass into the "SuppressAnalyticsCookie" processor.  If the cookie name ever changes in the future, we can edit the value in config and not have to touch the code.

In those processors, I make a call to the consent provider to see if I need to suppress the cookies or not.  If I do, I search the current Response and remove the cookies.  If consent has already been provided on a previous request, the code is skipped.

Note: Via Sitecore.Stackexchange, it was recommended that I not disable the Tracker.  I disabled it anyways, because even though it might be legit to have the cookie written on the first load per EU specifications, my client's requirement was that it was not to be written at all until consent is provided.  Additionally, I wanted to avoid editing the web.config file so instead of utilizing the HTTP module method, I wrote a pipeline processor.  (Yeah, I know I edited the web.config with the consent provider . . . )

So the processors take care of the Sitecore-specific cookies that are written.  For any controller methods that end up writing scripts that emit cookies, I simply decorate the method with my Authorize Attribute override and no further code changes are required!

Additional Resources

Ways to block SC_ANALYTICS_GLOBAL_COOKIE from being deployed - Sitecore.stackexchange.com

How to Write a Provider Model

MVC Filters

May 4, 2017

Help! No one can log in!

May 4, 2017

No one can log into Sitecore; Sitecore Domain deleted.

Oh snap.  It's Friday afternoon at the end of a long week and you get a desperate email from the client indicating that none of the logins for the Sitecore CM server work any longer.  Guess I'll put down the beer from the office kegerator and go look at things. :/

First up: Confirm the CD servers are still running/displaying the site.  Check.

Next: Is the CM server up?  Check; I can see the Sitecore login screen.

Next: Attempt to log into the CM with my admin account: Hmm, not able to log in.

Next: RDP into the CM server to see what's going on.  Able to RDP fine.

Next: Look at the logs.  Come across something interesting/scary:

Err, that doesn't look good.  Someone logged into Sitecore using an admin account and managed to delete the Sitecore domain.  You know, the one that *all* the logins are associated with. . .   So now, no one can log in.

Fortunately, the users themselves are not deleted when a domain is deleted. (https://doc.sitecore.net/sitecore_experience_platform/setting_up_and_maintaining/security_and_administration/users_roles_and_domains/create_and_edit_a_security_domain#_Delete_a_domain)

Before we get into how to solve this, let's talk about a couple of security issues here.

One: It looks like users that clearly don't know what they are doing have access to an admin account where they can do very bad things.  I think it should be a extremely rare exception where a content author needs this level of access.  It takes longer to properly configure security so that content authors can do everything they need to do but don't have access to portions of Sitecore that they don't need, but that's how it should be done.  Sometimes you can't avoid this and have to create some admin accounts for users, but as the logs show us, we don't even know where to point the finger since a 'generic' admin account was used.

Two: DO NOT GIVE CREDENTIALS TO GENERIC ADMIN ACCOUNTS!  If a user 'needs' admin access, make the account tied to that user an admin.  This would at least let us see who made the boo-boo.

Okay, let's solve this.

Restoring last night's database backup could be an option.  (Checks with Devops)  "Oh, the database backup process isn't running properly and we have no backups?  Super."  Side note: Well, at least we found that out now and can fix that.

Somehow hacking in to the database to restore whatever was deleted could be an option.  Hmm, I'm not sure I even want to go there.

Recall that there is a "Domains.config" file in the "App_Config\Security" folder and that the error specifically mentioned modifying it, there might be something there. . . .

After comparing the modified file to a stock Sitecore Domains.config file, it was easy to see that the only thing missing was the line that declared the "sitecore" domain:

I added that line back in and poof!: Everyone can log in again.  A much easier solve than I would have thought for something that appeared to be very scary.

Next step: IMMEDIATELY change the generic admin account credentials and let the users know that they can log in again.

Now, back to that beer. . .

TL;DR: Put the "sitecore" domain declaration back into the "Domains.config" file, drink beer.

February 28, 2017

Sitecore Server Switch

February 28, 2017

Sitecore Server Switch

One of the most time-consuming aspects of installing and configuring Sitecore is configuring the servers for their specific roles.  Per Sitecore documentation, specific configuration files should be enabled or disabled for a Content Management (CM) server and likewise, specific files should be enabled or disabled for Content Delivery (CD) server.  Failing to configure the servers for their roles can cause Sitecore to not perform as expected.

Note: There are additional roles for server configurations such as Processing, Reporting and Publishing.

Sitecore's ARM templates look to solve this problem in that they can quickly spin up and configure a Sitecore server, but this only works when using Azure PAAS.  For those not using Azure PAAS, manually adjusting the configuration files can be time consuming.

Sitecore Server Switch looks to solve this issue by using PowerShell.  After installing Sitecore on a server, you can run the "Sitecore_Switch_CM_CD_Lucene_Solr.ps1" script to configure a server as a CM server, as a CD server and additionally configure it to utilize either Lucene or Solr indexing.

Sitecore Server Switch currently supports Sitecore 8.0 (all versions) and Sitecore 8.2 Update 2.  More versions are coming soon.


Sitecore Server Switch GitHub Source
Sitecore Server Switch on the Sitecore Marketplace (coming soon!)


Big shout-out to Sarkis Einarsson for his PowerShell scripts that were the base of this project. (http://sitecoreunleashed.blogspot.com/2015/09/sitecore-8-toggle-cd-configuration.html)

Additional Reading

You can also take this to the next level and automate everything.  Check out Patrick Perrone's work into this space with his automation scripts.

Update (03/01/2017)

And of course, how it usually happens, after writing this module and post, I came across this site that has some automation scripts for 8.2 and even includes the ability to configure additional roles: https://bitbucket.org/sitecoreautomationteam/sitecore-automation/wiki/sitecore-environment
Note: I have not tested this.

And I've also been made aware of Michael West's PowerShell scripts as well: https://gist.github.com/michaellwest/d1124a459cb1fb47486f87d488ecfab8