CONTENTSTART
EXCLUDESTART EXCLUDEEND

Killing Kentico Softly, With Your Development

Many are familiar with the phrase “Death by a thousand cuts.”  It carries with it the idea that you can do a lot of damage with a sum of seemingly minor things.  This isn’t going to be a tutorial, but more of a warning with other developers on the dangers of taking shortcuts, and breaking Kentico with your code.

This issue is near and dear to me, because in my time working with Kentico, I’ve inherited a lot of other people’s messes.  I’ve had upset customers who had websites so poorly made that it made Kentico look lame in their eyes, because they could neither see nor use many of the rich features of Kentico.  In this article, we’re going to cover some major issues I’ve had to fix, and tips to help you be a better Kentico developer in part, and a better software engineer as a whole.  It may not be fun, but this really is an article that I believe every developer needs to read, and take to heart.

Breaking Major Feature 1: Staging

Quick Intro on the Staging Module

Kentico has many features in it, but one that is woven into pretty much all of Kentico is the Staging Module.  This module allows users to have a Staging / Development environment, and push changes to production environments.  But you can't just copy data from one environment to another, because most object's in Kentico have their Identification linked to their tables auto-incrementing Row ID, which can change from one environment to another.

For example, say you have 10 Contacts, with Contact IDs 1 through 10.  When you add a new Contact, that Contact's ID will be 11.  Now say you have a Staging and Production Site, and both have the same 10 Contacts.  On the Product site, 2 new contacts are added (ContactID 11, and 12).  On the Staging site, one contact is added (which will be ContactID 11).  When you push your contact from Staging to Production, that ContactID 11 now becomes ContactID 13, since there was already an 11 and 12 and that is the next Row ID.

Now if I update my ContactID 11 on Staging (try to stay with me!), the Staging Site says "Update Contact 11" but the Production Site needs to hear "Update Contact 13" instead.  What happens in the background is Kentico passes along more than just the Contact ID, it also passes along the GUID, CodeName, and SiteID (if available), so when the Production Site gets the Update, it can hears "Update Contact 11 with CodeName 'BillyBob' and GUID abcd-1234-1234-1234" which the Production site can then say "My Contact 11 isn't BillyBob, but my Contact13 is, so I'll update Contact 13."

Now that (hopefully) you get the concept, let's go onto how we can break it!

How It Gets Broken 1: ID references

The first way we can "break' the staging module is by housing ID references in areas that Kentico has no way of knowing how to translate.  An example, say you have a Dealer Page Type, and it has a comma separated list of StateIDs in a text value that show which areas they service.

When that Dealer gets pushed from Staging to live, the text value of "1,4,6,23,74" doesn't magically change, Kentico only knows it's a text field.  On your production site, State 23 could be different than the State 23 of your Staging environment.  Congratulations, now the staging module is broken for your Dealers, because when you push the dealer, now they are servicing different states.
The solution to this can vary, but a quick solution is when referencing something in a character-delimited list, to NOT us IDs.  Use something that doesn't change, like the GUID, or the CodeName.  While this doesn't prevent reference breaking if that referred object is deleted, it does prevent having a reference point to something it's not suppose to point to when pushed to different environments.
Ideally the best way to house these relationships is through either existing Kentico systems that are already set up with Kentico features (such as Related Pages or Document Categories), or to use a properly set up Custom Module Class (either a Binding Class, an Extending Class that extends an existing object, or just your own Class).  More on this ahead.

How it Gets Broken 2: Custom Modules not Configured for Staging (or Continuous Integration)

As you progress in your Kentico prowess, you will no-doubt discover the awesome power of Custom Modules.  You can create your own User Interfaces, Classes, settings, and more, all within Kentico quick and easy.  However, just like pretty much every Kentico made module is tied into and configured for Staging, this means that yours needs to be too.
Whenever you create a new Module and add your own classes, you can add ID references to other Kentico objects.  For example, you could have a Dealer module Class that has a CountryID for where that Dealer is located at.  This leads to two mistakes that break the Staging module for your class.

Mistake 1: No Reference

In your Custom Module Classes, if you make any field an Int type, you will see a special drop down "Refers to."  This is how you can create references between your class and other classes.  The first mistake I've come across is this is not set, meaning the developer never set the "Refers to" so Kentico has no idea if that number you put is a reference to a Country, if it's the current year, if it's your age, or whatever.  It only knows it's a number.  So when Kentico pushes your object, it will not make any attempt to find the object you are referencing and provide the vital GUID, CodeName, etc so the other environment can match it and translate the Row ID.

This is simple to remedy.  When you have a reference, just tell it what you are referring to in the drop down, and set the reference type.  Next you need to generate the TypeInfo of that class, which Kentico provides you the Info and InfoProvider auto-generated by clicking the "Code" field on the left.  Sweet!  Either click the "Save Code" to save those classes to your App_Code, or copy and paste it into custom classes in your Module project.  But, the job's not done yet.

Mistake 2: No Staging Module / CI in the Type Info

This is where things tend to break again.  People stop at the generated Info Code.  They ignore the Warning comment that says "Hey, you may need to configure this TypeInfo, don't forget to do that," and they go on their marry way.  News Flash everyone, Kentico gives you the starting point, but you need to add your configurations to the TypeInfo for your object, including if this item works with the Staging Module, Continuous integration, and how.

I've come across projects where a custom module was created, and while originally there was only 1 website, now they want a staging environment, and I have to tell the client "Well, normally it would be super quick to set up a staging environment….but now it will take a bit because I have to go through all the custom classes they created and enable it, because they didn't."  - Bummer.

The solution to this is to just add in the proper configurations.  Kentico is magical, if you configure your TypeInfo properly, it really does the rest for you.

Kentico provides all the documentation and references, so make sure to read up on them!

Breaking Major Feature 2: Localization

Another big feature in Kentico is the ability to translate your content into multiple languages.  And while it may not be an initial requirement, you always need to have this feature in the back of your mind.

How it Gets Broken 1: Document references instead of Node

A page is a Node (CMS_Tree), while a language version of that page is a Document (CMS_Document).  If you have any references to Documents, as soon as the site takes on a new culture, now you have a problem.  You really meant to link something to the Node, but now you’re tied to a specific document culture.  For example, say you have a Binding Class of all the Dealers that service a particular product (found on the Content Tree).  The binding class has a reference between the DealerID, and the Product’s DocumentID.  When that Product is translated from English to Spanish, and you go to look up all the dealers that service that product, there won’t be any because there aren’t any Dealers assigned to the Spanish Document, only it’s English sibling.

Whenever you are referencing an entity on the tree, regardless of it’s culture, it’s important to use Node references.  Whether that is the NodeAliasPath (which remains the same across the languages) or the NodeGUID, referring to the Node will prevent things from breaking, as all the Document language variations will share the same Node information.

Another option is to use the Related Pages feature, which relates NodeIDs, so again they won’t break across languages.

Document Categories

With Multi-Culture in mind, you must be very careful using Document Categories as they are.  Document Categories are linked to the DocumentID.  A single Node can have multiple Documents, one for each language, so if you are using Document Categories, you can end up with Categories being difference across the various language variations, and a change to categories of one language variation won’t replicate to the others.

I recommend using a custom Module Binding Class that has a NodeID and CategoryID reference, and use my Advanced Category Selector in order to manage the relationship.  If this is not possible or too advanced, just know you’ll need to adjust any WHERE condition that uses the CMS_DocumentCategory to leverage the Node in lookups.

Something like:

NodeID in (
     Select NodeID from CMS_Document where DocumentID in (
          Select DocumentID from CMS_DocumentCategory where CategoryID in (
               Select CategoryID from CMS_Category where CategoryName in (‘Category1’, ‘Category2’)
          )
     )
)

Instead of

DocumentID in (
     Select DocumentID from CMS_DocumentCategory where CategoryID in (
          Select CategoryID from CMS_Category where CategoryName in (‘Category1’, ‘Category2’)
     )
)

How it Gets Broken 2: Hardcoded Text

This one I admit I’ve made the mistake of in the past: Hard coding text.  Whether it’s field labels, error messages on custom web parts, or Static Text in Page Templates, it’s so very easy to just type in what you want it to say, in English, because everyone speaks English, right?

Well that’s the mindset that often breaks Localization.  You can localize the content of the Page Form, widgets and the like, but if a developer hardcoded English right into the page template or web part layout, then there’s little you can do.  What’s even worse is if the content is in the code-behind of a web part, you can’t even use a custom Layout to overwrite it!

The Solution lies in another tool Kentico provides, the Localized Resource string.  In Kentico you can create resource strings through their Localization application.  These resource strings (example “mysite.scrolltotop”) contains any number of language versions of what they represent.  It acts as a virtual resx file, allowing you to add 1 resource string that will then translate into the proper text depending on the current language.  You can translate resource strings through the macro mysite.scrolltotop, or through the method ResHelper.LocalizeExpression(“mysite.scrolltotop”). 

Whenever you are adding Static Text, hard set labels, headers, or code behind, even if it’s a daunting task, make everything that may be translated into a resource string.

Learn more about how to Localize User Interfaces here.

Breaking Other Features

This last area is kind of the “catch all” of breaking other things, because it covers breaking anything else.  This is mainly around when we need to customize Kentico itself, or it’s processes.

Breaking Webparts

Webparts are the building block of the Portal Engine.  There inevitably comes a time when to meet some client requirement, we need to modify how a web part works.  So, the miss-guided developer may just go into the web part code, and make their adjustments and leave it.  Then when upgrade comes around, you have a whole bunch of items that Kentico is trying to upgrade, but can’t because it’s been modified.

Always resist the urge to modify web parts.  Instead, clone the web part and put it in a custom folder.  This way you can make your adjustments without hurting the normal web part, and come upgrade you have a before and after you can use to help upgrade your modified code.

Breaking UI Pages

I’ve recently had to modify (heavily) some of Kentico’s default User Interfaces.  There are many ways you can customize and modify UI pages that Kentico provides, but there are very few ways you should.

Firstly, when possible, take the User Interface Extender Class approach.  Most UI pages leverage things like the UniGrid, UniSelector, and Kentico’s User Interface engine often allows you to customize a User Interface and provide an Extension class that you can modify the behavior of these Uni tools simply through the class.

Secondly, if the User Interface references an ASPX page, take the Clone and Modify approach.  Clone the page, make your adjustments, and customize the User Interface and point to your modified page. 

Thirdly, if the above fails (because there are some cases where you can’t just clone something to change the behavior), if you must customize a piece of Kentico core, properly comment and mark your customization, ensure that you are catching any potential errors, and make sure your customization is limited to only the things that need it.

Breaking Events

I can’t remember the last site I did where I did not have to add a Custom Loader Module so I could hook into system global events and modify their actions.  But one danger in modifying these hooks, is you can accidently cancel or break event chains if your code throws an exception and you do not catch it properly.  This can have some very weird and hard-to-diagnose affects depending on what processes is being cut short.  Make sure to properly try/catch your code so it does not break.

Breaking Default Functionality / Management for a Small Use Case

If ever your requirement goes like this “in this certain case, I need _____ to behave differently” then make sure it only behaves differently in that case.  And make sure that “that case” is something the user can manage.
The best way I can describe this is in a recent example.  A customer needed a Kentico Report to export as a CSV without the Head row, and with some special formatting.  They only needed this requirement for (currently) 1 report.  There was no way for me to clone and modify in this case, as the modification went down deep to protected classes that I could not rebuild, so I had to modify it in the existing implementation.

I could have modified the Kentico Reporting engine and removed the header and did the special encoding for all reports, that would have been easy, but then I would have killed all the rest of the Kentico reports.

I also could have just hard-coded the report name so it only did the special formatting on the 1 report that currently it was needed on.  That may do the trick, but now I’ve limited the user to where they have no ability to set additional reports (if needed) to have the same operation done.

So the proper solution?  I added a Settings Key that allowed them to specify which reports they wanted to behave in the unique way and adjusted the code to leverage that.  Now I have neither broken Kentico’s reporting engine, nor have I limited the user’s ability to manage.

Always leverage Kentico Settings when you have something that may need to be managed.  Hard coded identifiers are paramount to Magic and often a sign of lazy coding and impossible to manage without touching code.  And avoid Application Settings Keys in the web.config for the same reason, they are very hard to manage, and in the spirit of Kentico, manage it in the browser.

Conclusion

The Kentico platform was one of the first CMS’s (if not the first) to boast that you can build and manage a site completely through the portal.  Every piece of Kentico is geared around managing it through the browser, with tools and features to make it the best platform, and most developer friendly.  Let’s all do our part to develop well, to keep that same mindset, so our Kentico sites are built in such a way that customers can leverage all of Kentico’s vast features.  Happy Coding!

Comments
Blog post currently doesn't have any comments.
= eight + six
CONTENTEND