fixing end of string expected at position error in Sitecore

Recently while verifying some of the components in Experience Editor, I observed that while adding datasource to the renderings, it was throwing “End of string expected at position 75” error.

I checked following things to troubleshoot the issue:

  1. If Datasource location specified in the rendering exists or not, and it exists.
  2. If Datasource template is defined in rendering or not, and it was there.
  3. Check the errors in log file, and there was an error there, please see the below screen shot what error it was.log
  4. Following error was visible in front end while adding datasource.end-of-string-error

 

It turns out Sitecore doesn’t like hyphens “-” in query path, and few other which includes:

  1. If you query contains keyword “and
  2. If query contains keyword “or
  3. If query contains hyphen “

In my case when i checked the query and it contains keyword “and“, which was basically failing and not allowing me to add  datasource from Experience editor.

Solution:

In order to fix this issue we can add escape character “#” before and after of the items that contains any of these keywords or hyphen “-“, so, for example:

Before : query:./ancestor::*[@@templatename=’SitecoreRoot’]/Global//Modules/Image and Media Module

After : query:./ancestor::*[@@templatename=’SitecoreRoot’]/Global//Modules/#Image and Media Module#

After making this change, the issue was fixed.

It seems this issue was there in Sitecore 6, but it can be replicated in Sitecore 8.1 (rev 160302) as well.

I have raised this with Sitecore support team, and they have registered this as Wish/Feature, so, we may see this issue resolved in coming product versions.

I hope this helps somebody.

Happy learning 🙂

http://sdn.sitecore.net/Reference/Using%20Sitecore%20Query/Sitecore%20Query%20Syntax.aspx

 

Versioned-Unversioned and Shared fields in Sitecore

This blog contains information about items, more importantly languages of items, versions of items in languages, and sharing the values of individual item in different supported languages and numbered versions.

This post is focused on explaining the differences between Versioned,Unversioned and shared fields in Sitecore.

Versioning of an item is controlled in three ways:

  1. Versioned fields
  2. Unversioned fields and
  3. Shared fields

VersionedFields

Versioned fields:

By default all the fields are versioned, which means the the field value will be different across different numberd versions and different language versions as well,
example of numbered version-  title/description and etc, or any such field whose field will be different across different languages/versions.

Unversioned fields:

There are certain fields whose value is same for all the numbered versions for a specific language, such as “Country Name”, “Employee Name” and etc, if we want to have such configuration, we should make that field as “Unversioned” in template definition.
When this(“Unversioned”)  checkbox is selected, the field has the same value for every numbered version within a language, but may have different values between supported languages.

Shared fields:

When the field value doesn’t change across languages and number versions, we should mark that field as shared, shared field value will be common across supported languages and versions.
When the Shared property is set, changes to the field value in any language or numbered version of the item will be reflected in all the other language versions and numbered versions.Example- “Id” or some unique identifier which will be commons across languages and versions.

I hope this helps someone in understanding the core concepts and versioned fields.

Please let me know your feedback, or your thoughts on the same.

Happy learning 🙂

Securing Sitecore connection strings

There are scenarios where we don’t want to compromise with the security of our application resources, this includes Connectionstring.config file as example, If someone compromised CD servers, they will have access to Connection string file and can use the details in a wrong way.

There are ways we can secure the connection string in Sitecore, in this blog post I would like to give an example of one of the approach which can be used:

Please see the sample code snippet, to encrypt and decrypt  connection string file.

Configuration config = WebConfigurationManager.OpenWebConfiguration(“~”);

// Get the connectionStrings section.
ConnectionStringsSection section = config.GetSection(“connectionStrings”) as ConnectionStringsSection;
if (section.SectionInformation.IsProtected)
{
section.SectionInformation.UnprotectSection();
}
else
{
section.SectionInformation.ProtectSection(“DataProtectionConfigurationProvider”);
}
// Save changes to the Web.config file.
config.Save();

Few points to note and consider:

  • This approach uses System.Web.Configuration namespace to work.
  • We access application’s web.config file via OpenWebConfiguration method.
  • We access connectionStrings section of the web.config file via GetSection method.
  • Encrypt and decrypt the sections.
  • Save the changes.

This is how the connection string.config file looks before encryption:

<?xml version=”1.0″ encoding=”utf-8″?>
<connectionStrings>
<!–
Sitecore connection strings.
All database connections for Sitecore are configured here.
–>
<add name=”core” connectionString=”user id=user;password=password;Data Source=(server);Database=Sitecore_Core” />
<add name=”master” connectionString=”user id=user;password=password;Data Source=(server);Database=Sitecore_Master” />
<add name=”web” connectionString=”user id=user;password=password;Data Source=(server);Database=Sitecore_Web” />
<add name=”analytics” connectionString=”mongodb://localhost/analytics” />
<add name=”tracking.live” connectionString=”mongodb://localhost/tracking_live” />
<add name=”tracking.history” connectionString=”mongodb://localhost/tracking_history” />
<add name=”tracking.contact” connectionString=”mongodb://localhost/tracking_contact” />
<add name=”reporting” connectionString=”user id=user;password=password;Data Source=(server);Database=Sitecore_Analytics” />
</connectionStrings>

After encryption, this is how the file looks:

<?xml version=”1.0″ encoding=”utf-8″?>
<connectionStrings configProtectionProvider=”DataProtectionConfigurationProvider”>
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2ivFsj7cNEaM7QEbCTKmIwQAAAACAAAAAAAQZgAAAAEAACAAAAAhdGbhIMVnCuLPqxeQN8XGwAwUQAAJxH+/HDKbuXL70AAAAAAOgAAAAAIAACAAAAA7VJJIQ1/LGK2vEPieVV4MAh7sFiLEBf0rbHNQOqObocAKAAA+aDLJMlW2QCwkMiggfcUmw6DQA+jCZ6ivu5VduLr7iKQgoI6XdAV4SRlaJt3mS4ZSWmFEsllJI/Gkw74cknBjV82msfjdne+RMWWCMmsyW3a6NmMmtS0g4mQBBBELnqDx8FzSyZfUbUnTkIMixLLLM9H3tVcVebMXSb4SRn4Vzsi2y2Ux+Has3AEKwNfUSf2cqWAqrqXz+WnusSZpEaS6U6RmkKe5P3llacPtfAQGoqTvrxDTF49V/H/q013ZUCLUO6r9GvALNv0v4Q4PWeUe64i2TCRVIRh2anAdlqUtkw1UWMQfbwJLeyGjn4SMAF2fC2ixnqKh29pbfvYe9vQqkDGda9SyRGNL3brWYeL3PS1rFLlL4bnMf3BORLL0rDGzqNwHoLczXB56VhzYRceALyB+tN303Mqv13U6UAUwz1E6e8fYw6sYNR5/b3PL5nYgew8sHKKMysHfmzLrgiLaRxtZOLFp8unus8V0K5BaJck6iprRtNJ7jJuZ5OkRWgYhZ4bXeWjN9z386NVaiPfLPypoyo8tP252+lGpWZID7WtbSkdDXpRZ5VzojiGDZRu+8/vyLfxACM6Or8u+b3YDqLfrBbQE/JklDjxx2ZriskU9lf1lkXgSFa45PlJAfCr8nIldNUvVojKZDmbUP5aiNqiI/0CiZ8RZLQ3N1zBsISdK3VTOOF5WYdulpRq3ZH4FvU/QtqY6Bk1mPrb59TIx/appq86SIcygQats1mHuZ0/WN4oCwVfhQaN9t91G7vinxYluR6ljZkBfky1x6HYNcj+TGp2WAK6Y9+ATiOfbOHs+vgunQA1575He3uHCxMZuU0YeU1HEE5xnEvhG3aiLMkUc0h5wX0vvjgen2WC5xpRbN4vL4sqvcSsGXIt/smkz/h+rvS2LTl+fXx1p+QSZnoA2Gi5ooAaO3W0egda21nACp08kAzSJAuiivIqvmEpDkrn41TCG2HFgh2WErcc42xCUnEZ80drTzjCTP0fW9Q/qmudnz8OioXt5Ftifzj44fpSZcfCzH0uVMiFrwwx9RNjdAiIBUxDaqnygRtHXh3eXw8ofIkHikMzwXXw8CB7WAaGfNgHJpICG8Uj4AgWzV9Q59FkG6RAjEAtAXU1vFGEQbRY+bbeGobI5epXMvZmJaLnhQmrlGDE4XgwwoaXmEkTxM16hukmXqcys3yUL1v+1VHsvAnA4Ng+aixOBaQjlGkXTv2zHEHH4NM1oAQ106KgpzuXbRNogv9RgRO2aXlaNAHmR3Z08v7hEmh9vwMdR7sSkKwP4RB3EIkF4ScBsFN++G8ZmdFVx7biPCx+tzXCUsy9yFWHQ/eqsfKuvVqVd6RzTpgLTXilTpT2gcqbDKMdm9qm2gUkuN/SvL7wyY1Lciuv/E65RS2EgoLOLZ5DOWe3YMOmTSnpwpqj4wXfeh+mbNJF9ejjXmceOUfr6BEvhMB3mjF8onJHFq1M+NV7EeW8i4SdGxiGxSURW9SVxB37Rkdikn4/TjIIE5n3+h90By9VIWuUgBRJ1XEy7HnUyShM/0WSyx89pvWU5LZeHr/mkLAdLyo4O+L8LdzC2QYsjy+fI/Smx2DuDyJvCJU/fE1ikm//G6va12uI/ZoeqCiJX4m9yKcNyJT8OrCzhKK4PN/P+05raC6oyu4qNvMunR4PGUc3BHziimAFuXkUjONfNlkwZn1CMdGmW8JOxycVXqS6QMhJpwBnOcyHKutnNFbKjlzXwhhiVEKZr46/MmTkrrePHyfpvjeZZwD3ZFsLr6O7qYd5qpv/7Jb9RPRKDFDqzc9XSdn0kLH8uYBQxrI7vVhQIgLIOwNbODXvtY0fjrrrfdzFm2zpIkxx164yZgBRDlSW1QhlSGMTzO+5sUO8LUb1aJiXHj4s/gITI5yX7AB8PBXAlgEikBcKJuiqq5IyiKdLWRNCEmmCI4ocVl/CliZ8iruK7S8ei+GFpCkOHBjrTwEcLM7vV+z0FT1zjJVLWJ4luurbQcvEiyNHUymeprpk6NKfj2b6WyZCAlzlpLWyNONfQCDtSYcfcXtwf8fmdOZeI0M43BlgnT8SHW/ujHrNWpVTofw1u+Iso8/Hy1BwgFMD1JXXvuEKK+DaQEqrP6zbPRHTgMXTayvQW5rmuWIUSOJGdUgEhMoTGIQs7jY5sv24PunM0wpYZag+EZm1LzqtWwmHB8IQ/DRqY1ioCiDY1xum/nk3OVblMnhAwH2mJHj7nsXY7+H45q4fSW21fy1dksQgAW9P8T+PQeLdflAhtmUQLBNEAmnXOk6pRvVmeJ60yHqQFV7N0KHnCnnPRu/YSZLQrjGoX0dz0s9jZRVzxiflc+241gQVE8ygk3uL0wtsDvoA7Dq1FNKgHSjffcmQoHaOA/ESt6TD0XM+IzIhtcSz3SAL0OB3JIVaMjF7vcK7INRPPsSR6s7wmm+0kjYfi/tueQq8O2dWxSvMl78bZvehfy7PdBhQ+70AgtKOeev5o/OUlI7/V9wEBYwVjkz053p2/B4tijOgjy/hygFsQry4gdZh8PsqeEAP8h4gXlJ76QgszffTI0K9Heiwc5ptwrP6BVr2j1wL95FI5mLgsMWLx1N+8G1rwvhM5DCSVtYqkPtwn9cjLaRgO+K5eyfDFHN+qyKNZ2seiJ0xl1DvUVK0Be5/FjRoCT1V4GLDLAGZ1hJKGoM5zYkh2XuF8soXQ8v+Hdzmb+JaEuXGvhLnTMuIC9Rd+dV/BFUfEscctONdlLfNmobqO83xxuxRIULEPVTXBEBrMXMRnmRa6HPm9KURgbGzJFiTn3oG1g+jCLYfVfE6FcF+29oPsXwsWy5EePdFJXcG7m4RmTYitlWApBE7SfqFhMLK+SXH1OZiZ2GvszIWp3NdEF0bpZXvM6MdFQgtzhrdWpEevRgaXCfFHBv2w3rh2II8hpYXVCs5Vpy7uHzZ99yZULWAZbd/PtuDOoaNtXcJQTxjJcHiAqivpDFknbHrbMwjjuByFjoPLqUCbaGDN0LsPV9yNNQaBXSCGMeW/5eSfLcMMjZHCM8j9rNXiixnc5/hk3kc8EpooduJBm8z1pxEI6rA2+zAZVsK9qH9ydiR/yt7EH+CEBZGmN411ybZiCyx1EHRxE1hL0lXbKzQAqLJQOthKukou5DNhlePPm+hDkV9Y1elPXOkO4tSrdoqTKd0XYNmUHeAcca1C7BOY5D6sRfn/4PHSM2NYdMIlkeIyLyMDnPzm1Wu/7ArAZJUJ7D8aLreeNsI3d48bIO/j/GtAdCLTN6sBo21R8B8tVv0qbbic83D8P8EDodHEJvxHUtEPBYjpFgloek+ToPnHH7aHUqSXtFH5EI1bbp2zNdVpwB4kJ9beC6Y8foR81NAKVlNjeCSVmrW2+ZUw19CSMLiiMh8DCG5jQvZ5ejBqmIGpHYZEIc5yQLxb5AlSbYeNEH6MsCh6mhpBfWXbTKJxUhZvFuaBh0+UgI0sRB4OQ2vJ0TtgRRnCiFnOlszBqiiEH5MbAFwWtFS/mNWPFi76HNN3YF2sL4QtrlqLw6TcVuGIRCqWkZasb7hdwdMarV0A8fCx996PwMsWPdQ8StVjXqz567yzev5d1T3tbSrzdo8HgLwIAIdLQOxP1JnNlnLqwXW0k37CFZyUjwWV3ZfiXJN1tIolul1ogf5modPF3YB0aS4FQAAAAB6C3jSoZh6M0ltHxa/HTqQUTSQexkdlI/5baIOQQx/dmJEoTWBa/D2JTHAwYhTQa/psamG03lzeKsm0jetzACw=</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>

If we want to decrypt the section, we can use UnprotectSection()  and can review the connections, once we are good we can again encrypt the setting using ProtectSection() method.

We can also secure the setting using aspnet_regiis.exe tool.

I hope this helps someone, who is looking for something similar, please let me know for any questions on this, happy to discuss more.

Happy learning 🙂

MongoDB authentication in Sitecore

Securing application data is critical for any client and business, and the same principle applies to Sitecore application as well.

One of the most important component of Sitecore is MongoDB, which is where we store all Experience(xDB) related data, MongoDB was shipped into Sitecore’s ecosystem from Sitecore 7.5, and it’s very important to make sure the data stored in xDB is secured, and only authorized uses has access to the data.

Recently, we heard about thousands of MongoDB data being hacked, so what’s the reason behind it? any guess? it’s simple- all those DBs were not configured to be secure and anyone can access it.

It would be great if MongoDB installation itself comes with an option, where we can secure our data while installing it, like how we do it for SQL.

Even though, we can go back and secure the data  by setting up users/roles and permissions, but it’s always great to do it it first place.

We also have to see and make sure that Connection string used for MongoDB is all protected with credentials, so that only authorized users can access it.

As part of this blog post, i would like to cover the steps which we can follow and make our Sitecore application more secure.

  1. Create MongoDB User
    1. Follow the below command to create the user.
    2. db.createUser({user: “mongoadmin”,pwd: “mongoadmin”,roles: [ { role: “userAdminAnyDatabase”, db:”admin” },{ role: “root”, db:”admin” }]})
    3. We just created a new mongouser- “mongoadmin” under database “admin”, and has given all rights to this user by assigning role “root“.
  2. Verifying the User
    1. If we want to verify that user gets created or not, we can use the following command for the same:
    2. db.auth(“mongoadmin”,”mongoadmin”)
    3. This should return 1, if the user is authenticated.
    4. We can also see the list of all users by running following command:
    5. db.getUsers()
  3. Assigning specific roles to unique collections
    1. We shouldn’t be giving “root” level access to the user, and it should be more specific to the database and collection.
    2. For example: we can give read and write access to analytics database in Sitecore.
    3. In order to do that, please login to mongo shell and switch to admin.
    4. use analytics
      db.createUser({user: “mongouser”,pwd: “mongopassword”,roles: [ { role: “readWrite“, db:”analytics” }]})

    5. In this case we created a new user called “mongouser” and assigned “readWrite” role to it, and is specific to “analytics” database.
    6. In the same way, we can do it for other three databases also.
  4. Connection string updates
    1. This is how the default connection string looks like:
    2. <add name=”analytics” connectionString=”mongodb://localhost:27017/sample_analytics” />
      <add name=”tracking.live” connectionString=”mongodb://localhost:27017/sample_tracking_live” />
      <add name=”tracking.history” connectionString=”mongodb://localhost:27017/sample_tracking_history” />
      <add name=”tracking.contact” connectionString=”mongodb://localhost:27017/sample_tracking_contact” />

    3. After making updates to connection string, and adding required username and password details to it, this is how it looks:
    4. <add name=”analytics” connectionString=”mongodb://mongoadmin:mongoadmin@localhost:27017/sample_analytics?authSource=admin” />
      <add name=”tracking.live” connectionString=”mongodb://mongoadmin:mongoadmin@localhost:27017/sample_tracking_live?authSource=admin” />
      <add name=”tracking.history” connectionString=”mongodb://mongoadmin:mongoadmin@localhost:27017/sample_tracking_history?authSource=admin” />
      <add name=”tracking.contact” connectionString=”mongodb://mongoadmin:mongoadmin@localhost:27017/sample_tracking_contact?authSource=admin” />

    5. This is how the format looks like:
    6. mongodb//[username:password@]host[:port]/database?authSource

If we are trying to access MongoDB without passing valid credentials, we get this error in the log, please see the screen shot for ref:

MongoAuthenticationFailed

Once we pass the valid credentials, this error will go off.

It’s always a good practice to authenticate MongoDB in local environment as well, this helps us in setting the habit for it and we can uncover any issues well in advance.

I hope this helps in getting the understanding about how we can secure and authenticate MongoDB, and how to create users/permissions for the same.

There is a great article in MongoDB documentation, around setting up auth for Mongo and setting up users, creating roles for the same, please consider reviewing this as well, this is great source of information.

https://docs.mongodb.com/manual/tutorial/enable-authentication/

Thanks, and please let me know for any questions, happy to discuss more.

Happy learning 🙂

Content managed favicon in Multisite Sitecore solution

There are scenarios when we want to configure content managed favicons for multiple sites in the same instance of Sitecore.

I have implemented this for one of our client, so thought of sharing the same with the community.

In my case, we have separate Site settings node created, which has all site related information, from the below screen shot you can see we created a new field called “Fav Icon” which is of file type.

FavIconField

Once we have the field available, content authors can content managed the fav icon based on the site requirement, please see the screen shot for ref:

Favicon

In the below code snippet, we are trying to fetch the sitenode based on start node(home node), and once we have sitenode, we are fetching site settings node from where we can get “Fav Icon” Url value.

 public static Item ContextWebsiteRoot
 {
   get
   {
     Item contextItem = null;
     Item webSiteContext = null;
     if (Sitecore.Context.Site != null && Sitecore.Context.Database != null)
     {
        Item homeNode = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath);
        webSiteContext = contextItem.Parent;
        // Use webSiteContext to get "Fav Icon" url...
        // We don't have to use .Parent, if "Fav Icon" field is created directly 
        // under home node.
     }
     return webSiteContext;
   }
 }

In my case we don’t have the Fav Icon in the site home node, so we need to add some rule to get the correct item, and then reading “Fav Icon” field, but just in case, if you are trying to add “Fav Icon” field directly in home node, you don’t need to fetch any parent Item.

Here is the simplified version of it, when “Fav Icon” is added to home node.

 Item contextItem = null;
 Item webSiteContext = null;
 if (Sitecore.Context.Site != null && Sitecore.Context.Database != null)
   {
      Item homeNode = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath);
      var siteFavIcon= contextItem["Fav Icon"];
   }

 

I hope this helps someone who is trying to implement something similar.

Happy learning 🙂

Configure Sitecore to update sitemap xml on CM and CD servers

In a scalable environment the expectation is to have multiple servers serving the content(s) to different regions(or sometime same region as well), the typical architecture is to have one content management server and atleast one content delivery server.

Sitecore provides “Sitemap XML module“, when we publish content(s) it will generate an updated Sitemap.xml file – basically, a listing of page(s) that a web-crawlers can use to crawl and index a site.

sitemapxml

When we have content delivery servers set up as part of the configuration, we have to make sure that Sitemap.xml file also gets updated to CD servers which doesn’t happen by default, only content management file gets updated.

In order to make sure we Sitemap.xml file getting updated to the servers, we have to add following  to events section:

<event name=”publish:end:remote”>
<handler type=”Sitecore.Modules.SitemapXML.SitemapHandler, Sitemap.XML” method=”RefreshSitemap” />
</event>

This will updated the Sitemap.xml file in content delivery servers as well.

Note: Please refer this URL which explains about the same issue and resolution-

https://www.captechconsulting.com/blogs/sitecore-sitemap-shared-source-module-configuing-for-scaled-environments

Happy learning 🙂

Resolving issue with page preview in Sitecore multisite configuration

With Sitecore multisite solution and architecture, we see site resolving logic might seem to work in unexpected ways sometime,and i encountered this issue couple of weeks back, here are few things to consider with Sitecore multisite configuration.

Let’s assume we have two sites in the same Sitecore instance, this is how the typical structure looks like:

pagepreview

In order to have two sites in the same Sitecore instance, we also need to add site node settings which is present as part of Sitecore.config now,this is how it looks:

<site name=”website” enableTracking=”true” virtualFolder=”/” physicalFolder=”/” rootPath=”/sitecore/content” startItem=”/home” database=”web” domain=”extranet” allowDebug=”true” cacheHtml=”true” htmlCacheSize=”50MB” registryCacheSize=”0″ viewStateCacheSize=”0″ xslCacheSize=”25MB” filteredItemsCacheSize=”10MB” enablePreview=”true” enableWebEdit=”true” enableDebugger=”true” disableClientData=”false” cacheRenderingParameters=”true” renderingParametersCacheSize=”10MB” />

<site name=”my-new-site”…../>

<site name=”website”….> is the default setting that comes with Sitecore, we need to add any new site added to system here, so that site can be resolved, in this example we added “my-new-site” to the list.

It is recommended to keep the Rendering.SiteResolving setting value at “true” for any multisite solution in order to ensure that cross-site links are built with the correct parameters.
Here, the Experience Editor and Preview mode are opened in the context of the site defined in the Preview.DefaultSite setting.

In my case, I observed the page preview mode was throwing error, and when i looked into the “Preview.Default” setting in Sitecore.config file, it was pointing to “website
We had “website” site node , but the location was no longer exists- it was pointing to /sitecore/content/home and we didn’t had any item in this location.

So, there are two options here to resolve this issue:

  1. Update the “website” site node start item in Sitecore.config to point to active site/ location or
  2. Update “Preview.DefaultSite” mode settings with an active site.

Note: Please make any updates to Sitecore.config using Sitecore patch file, so that we don’t mess up anything with the default configurations, and down the line didn’t face any issue with Sitecore upgrade as well.

Also, to make sure that items are opened in context of correct site, we must set the Rendering.SiteResolvingMatchCurrentSite setting to “true”

All configs related to site resolving can be found (“/App_Config/Sitecore.config” in Sitecore 8.x and web.config in 7.x)

Please read more about the site resolving here- https://kb.sitecore.net/articles/986056

Happy learning 🙂