Mcbeev.com

Taking it one line of code at a time

Reader Tip - Cick the Hide Sidebars link at the top right toolbar on this page to give you some more room to read this post on the screen, because it is code heavy.

The Kentico CMS WebService is a little known tool when it comes to the functionality of Kentico. It is used in a few Web Parts, the Repeater for web service Web Part comes to mind right away.

The WebService’s job is pretty straightforward, it’s main purpose,  as you could probably guess, is to return data from the Kentico database via a SOAP call.

In this post I will show you how to enhance the built in functionality to also allow you to directly call the Kentico WebService from a JavaScript method. This is useful if you want to have some rich interaction on the client side of your pages such as creating a dynamic menu on the fly, or creating a rotating image banner.

To start out, notice that there is an empty WebService sitting around in your project already. It is located at:

~/CMSPages/WebService.asmx

Now if you aren't familiar with an ASP.NET WebService notice that this looks like an empty page. That is OK. There only needs to be one declarative that states the class name and where the code for the class exists. The code behind file for the existing WebService is located at:

~/App_Code/CMSPages/WebService.cs

Opening that up shows us a pretty basic example of a WebService.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebService : System.Web.Services.WebService
{
    /// 
    /// Constructor.
    /// 
    public WebService()    {    }


    /// 
    /// Returns the data from DB
    /// 
    /// String parameter for sql command
    [WebMethod]
    public DataSet GetDataSet(string parameter)
    {
        // INSERT YOUR WEB SERVICE CODE AND RETURN THE RESULTING DATASET

        return null;
    }
}

Now lets make some changes to this WebService code that allows you to call it as a ScriptService. And don’t worry, all of the changes that we make won’t break any of the ways this service is used already in the system, we are only adding functionality.

What’s a ScriptService you may ask ? Well a ScriptService could be defined as a special kind of WebService that knows to serialize each web method’s results as JSON.

using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Script.Services; // ADDED

/// 
/// WebService template
/// 
[ScriptService]	// ADDED
[WebService(Namespace = "http://webservices.mcbeev.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebService : System.Web.Services.WebService
{
    /// 
    /// Constructor.
    /// 
    public WebService()    {    }


    /// 
    /// Returns the data from DB
    /// 
    /// String parameter for sql command
    [WebMethod]
    [ScriptMethod] // ADDED
    public DataSet GetDataSet(string parameter)
    {
        DataSet ds = null;

	//Code to query the CMS Tree and Return Items


        return ds;
    }
}

You can test out your changes to see if it worked by building the web site and running it. After the start page opens up navigate to:

~/CMSPages/WebService.asmx/js

// if Debugging is enabled on web.config you can also hit

~/CMSPages/WebService.asmx/jsdebug

You should get a response that includes some dynamically generated Javascript that looks like this: (the jsdebug version is not minimized and includes more info, as well as your comments)

var WebService=function() {
WebService.initializeBase(this);
this._timeout = 0;
this._userContext = null;
this._succeeded = null;
this._failed = null;
}
..... abbreviated ...
WebService.GetDataSet= function(parameter,onSuccess,onFailed,userContext) {WebService._staticInstance.GetDataSet(parameter,onSuccess,onFailed,userContext); }
var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;
Type.registerNamespace('System.Data');
if (typeof(System.Data.DataSet) === 'undefined') {
System.Data.DataSet=gtc("System.Data.DataSet");
System.Data.DataSet.registerClass('System.Data.DataSet');
}

This result we are now seeing is a generated JavaScript proxy class. It takes care of the heavy loading and serialization of methods, responses, and complex types so that the client side knows how to handle using each of these responses and/or complex types. The key is at the bottom, the WebMethod we made above is now callable via WebService.GetDataSet(…) as long as we have this reference on our page somewhere.

<script type="text/javascript" src="/CMSPages/Webservice.asmx/js"></script>

Note that if your ASPX Page Template or Portal Page Template has a ScriptManager control on it, you wouldn't have to manually add in the script src tag. You could add a reference to the WebService in the ServiceReference collection property of the ScriptManager. Next, we need to write the followign JavaScript somewhere on our page:

function testService(){
	WebService.GetDataSet("", serviceSuccess, serviceFailed);
}

function serviceSuccess(results){
	if(results)
	{
		alert("worked");
	}
}

function serviceFailed(error){

     alert("Stack Trace: " + error.get_stackTrace() + "/r/n" +
          "Error: " + error.get_message() + "/r/n" +
          "Status Code: " + error.get_statusCode());
}

The testService method above would hit our new ScriptService asynchronously, and it should either return a result that alerts us that the call worked, or that it failed and why. There is a ton more you can do with ASP.NET AJAX WebServices but I don’t want to get off on a tangent on that. Just click through that link and read up more on it if you are interested or need more guidance.

Now lets put together a final example that grabs some data from the Kentico system and returns it to the client side JavaScript.

Modify the WebService.cs class to add in this WebMethod as follows:

    /// 
    /// Returns node names from the CMS Document Tree based on the path parameter
    /// 
    /// Path to search the Tree, such as /%
    /// List of TreeNodes
    [WebMethod]
    [ScriptMethod]
    public List GetTreeNodeNames(string PathParameter)
    {
        if (string.IsNullOrEmpty(PathParameter))
            throw new ArgumentException("No PathParameter specified");
        
        int cacheMins = 30;
        bool condition = true;
        string cacheKeyName = "GetTreeNodeNames";
        DataSet ds = null;
        List treeNodes = new List();

        using (CachedSection cs = new CachedSection(ref ds, cacheMins, condition, null, cacheKeyName, PathParameter))
        {
            if (cs.LoadData)
            {
                // Get from database
                ds = TreeHelper.SelectNodes(PathParameter, false, "CMS.MenuItem");

                //cs.CacheDependency = ...
                cs.Data = ds;
            }
        }

        if (ds != null)
        {
            if (ds.Tables.Count > 0)
            {
                foreach(DataRow dr in ds.Tables[0].Rows)
                    treeNodes.Add(dr["NodeName"].ToString());
            }
        }

        return treeNodes;
    }

A few things to notice here. I'm using Martin Hejtmanek's new caching method to get my DataSet of information. There is really no reason to make a direct SQL call every time, so let's leverage the really awesome capabilities of the Kentico caching mechanism. This method exists in Kentico CMS 5.5+ only. Make sure you update your site before trying this.

Secondly, I’m not using the input parameter to specify some information so our new script, i.e the NodePath, we will have to pass that along as well in the JavaScript.

Thirdly, automatic serialization of complex types is great, like a DataSet, but the whole DataSet object that Kentico’s API returns is fairly heavy. Don’t try to return the whole thing in real world examples. Remember we are trying to keep this thing lightweight and fast. As you can see above I extract out the property I am looking to use and return in a more simple List of string. You can make your own DTO object here if you would like. A simple DTO should serialize just fine.

Updated JavaScript example:

function testService(){
	WebService.GetTreeNodeNames("/Examples/%", serviceSuccess, serviceFailed);
}

function serviceSuccess(results){
	if(results)
	{
		var nodes = results;

		for (var i = 0; i < nodes.length; i++) {
			alert(nodes[i]);
		}
	}
}

function serviceFailed(error){

     alert("Stack Trace: " + error.get_stackTrace() + "/r/n" +
          "Error: " + error.get_message() + "/r/n" +
          "Status Code: " + error.get_statusCode());
}

And there you have it. Your test page should now be alerting some page names.


This post applies to using Kentico CMS 5.5+ and Visual Studio 2010. It may seem a bit basic to some veteran Kentico developers, but I had to show a few co-workers the ins and outs of setting up a solution the right way. Once they had it, they were able to debug through a local project that contained both the core Kentico code and the extra sample code for creating a custom event handler.

You might have a question on what exactly a CustomEventHandler is for Kentico. Well it is basically an avenue that once you have it setup you can extend and customize Kentico to do things that it can’t do out of the box. There are a multitude of events that your code can subscribe to and give you the ability to do whatever you want on almost any action in the CMSDesk and beyond.

The first part of the process involves installing the base product from Kentico. If you don't have it already, go ahead and grab the download of the Web Installer from Kentico's site. Once you get it downloaded double click on KenticoCMS_5_5.exe. Click Next at the welcome screen and accept the licensing terms, and then you should be asked for a location to extract the download to. I generally leave the default path alone, for the most part it makes sense to put it in Program Files. Click Next after you have chosen your path and it will extract to that location. After that is done we get to the interesting part. You should now be at a screen like:

_web_installer_1 

There are a couple options for how to install the Kentico Product, but for the purposes of this post we are talking about using the ASP.Net Framework version 4.0 and Visual Studio 2010. Choose the first option and click Next.

Next we are asked the location of where we would like the code for Kentico CMS to be installed. For our purposes we want to choose the second option, I want to use the built-in web server in Visual Studio 2010.

_web_installer_3

The next option is where we would like to place the code that we will be running locally.

 _web_installer_4

After that we are presented with a choice of Full Installation or Custom. Go ahead and pick the Full option, it includes everything, especially the Custom Event Handler starter code that we need.

_web_installer_5

After clicking next the installation will really kick in and copy all the files you need. Go grab a cup of coffee because this step takes some time. There are a lot of files to copy.

_web_installer_6

 If it all works out, and it should you should finish up at a screen that says complete and has a link to your new solution.

_web_installer_7

Click that link to open up your solution in Visual Studio 2010. The solution that the web installer created should open up and it should be fairly easy to see that there is one web site already part of the project that contains all the code required to run a Kentico CMS site. Pressing F5 will start the web site up via the built in Visual Studio 2010 web server and start you on your way to debugging.

Now the first time you run the code you will be asked to setup the database for a new installation of Kentico. That is sort of outside the scope of this post, and Kentico has a great support document already created that you can check out for help with if you need it. But at this point we have successfully installed the core product locally and can debug it.

Now that we have the base solution and website ready lets get into the real reason we are here though. And that reason is how do we add on the Custom Event Handler starter code so that we can customize various events inside the Kentico system.

Head back on over to the location of which you installed Kentico to on you drive. If you notice in the root Kentico folder there is a sub folder called CodeSamples. Inside that folder is where our target code lies.

location_folder

Since I’m a C# guy I will be choosing the project CustomEventHandler in here. That folder contains a class library project that has some starter code for us. Now back to the Visual Studio environment. Go ahead and right click on the Solution named Kentico5-5 in the Solution Explorer and add in an Existing Project. When the browse window comes up choose the CustomEventHandler.csproj file in your CodeSamples/CustomEventHandler folder that we just mentioned.

Kentico5-5 Microsoft Visual Studio add existing project

Now at this point the tricky part comes in with the fact that the Sample Code is a bit old. Hint, hint Kentico devs!  Visual Studio will want to convert the project to a .NET Framework version 4.0 project and set the target framework property for you via the Conversion Wizard. Just click Finish and ignore the conversion log.

At this point you are probably saying great, why do I now have like 14 warnings and the thing doesn't look like it will build. That’s because the the reference names are wrong for the sample code.

Kentico5-5 Microsoft Visual Studio references

Just go ahead and right click remove the broken references below

  • CMSHelper
  • DataEngine
  • GlobalEventHelper
  • GlobalHelper
  • SettingsProvider
  • SiteProvider
  • TreeEngine

And after that add back in the correct ones from the list below. The files can commonly be found in your new bin directory normally located at C:\InetPub\wwwroot\KenticoCMS-5-5\Bin\

  • CMS.CMSHelper.dll
  • CMS.DataEngine.dll
  • CMS.GlobalEventHelper.dll
  • CMS.GlobalHelper.dll
  • CMS.SettingsProvider.dll
  • CMS.SiteProvider.dll
  • CMS.TreeEngine.dll

As you add back in references you will probably get a strange error talking about .NET Framework versions. Answer Yes to that. It will convert the reference to correctly use version 4.0.

Kentico5-5 Microsoft Visual Studio add CMSHelper

After you fix up all the references your complete solution should build.

Next, to be able to debug into the Custom Event Handler code, you will need to add a reference from the output of the class library project to the Kentico website in the solution. So on the bin directory right click it and choose Add a Reference. Then select the Projects tab and choose the only one in there, CustomEventHandler. This will tie the two projects together.

I should mention here that at this point if anything went wrong, like the project still isn't building go ahead and double check the target framework of the class library project (Right-Click Properties on the project) and make sure the Target Framework is set to .NET Framework 4 like below.

 Kentico5-5 Microsoft Visual Studio Target Version

Next we need to let the instance of Kentico know that we are using CustomEventHandlers. To do so we need to add an application setting in the root web.config like so:

<appsettings>
	<add value="C#" key="CMSProgrammingLanguage" />
	<add value="http://localhost/WebService/webservice.asmx" key="WS.webservice" />
	<add value="CU05-20100804-YfqLrL" key="CMSTrialKey" />
	<add value="true" key="CMSUseCustomHandlers" />
</appsettings>

As Kentico states in the documentation for this key you may change the name of this key also. Check out the docs here for how to do that.

Save and close your web.config and you should be all set with that. There is one last step we need to do, and that is set a break point on some code. One of the most common methods to use in the CustomEventHandler is the OnAfterUpdate method in the CustomTreeNodeHandler class. Add in the two lines below to have something to test with into the method. Set your breakpoint on the second line.

/// Summary
/// Fires after updating tree node in the tree. Performs the additional actions after the tree node is updated.
/// 
/// TreeNode object to update in tree
/// TreeProvider object to use
public override void OnAfterUpdate(object treeNodeObj, object tree)
{
     // INSERT YOUR CUSTOM AFTER-UPDATE CODE
    TreeNode node = (TreeNode)treeNodeObj;
    string nodeName = node.NodeName;
}

Set the web site as the Startup Project and then go ahead and hit F5 to run the solution. Since this is the second time you have run the site you should not be prompted to setup a database again. You should go to the default page of the site you first created. Login to the CMSDesk and then edit a page in the Document Tree. As soon as you click the Save button the OnAfterUpdate event should fire on your updating TreeNode and  debugger should pick it up.

Kentico5-5 Microsoft Visual Studio Debugging

I hope this tip helps you out. I think my co-worker owes me a beer for writing this all out for him.


9 Reasons Why Your Company Needs a Wiki

I’m a huge fan of using a wiki to help organize our business and it’s various functions, such as creating software. In fact I usually end of recommending the use of a wiki to almost all my clients who do not already have one.

If you are not familiar with what a wiki is, let me give a simple definition right here:

A wiki can be defined as a web page or web site that allows a community of users to add, edit, and maintain content that usually surrounds a given topic or topics.

It’s a simple concept really, a web page that holds content, that’s almost no different than any web page out there. But the real difference between any old web page and a wiki is that anyone can edit a wiki right on the fly, while a web page is normally maintained by one person. In fact the more users that a wiki has adding and editing content, the more relevant and informational that wiki will become.

Now that we know what a wiki is let me get on to why I think your company could be better off than than it is today if they implemented a wiki.

1. Knowledge is power

Collecting all the pieces of of knowledge from a persons head is a tough thing to do, no matter what the topic or project is. The old saying goes there is no substitute for experience and I firmly believe that. However, having a place that becomes the fountain of knowledge for you organization is very powerful.

The wiki becomes a repository of knowledge that all employees can contribute to and learn from. The more knowledgeable your employees are, the better they can serve your customers, the better they can perform their jobs, and hopefully that leads to making the company more profitable.

2. Documentation can save your life in a pinch

One of the most common uses of having a wiki is to have a single point to document a company, client, or project. The benefit of having a wiki page on a topic, such as a project, is that it is easy to add and maintain small pieces of documentation that sometimes don’t fit well into a full blown spec or project plan. Or sometimes just having a link that gets you to right location of the project plan or latest revision of a quote can be just as important.

Tools of the Trade - by buffcorephil

A real world example of how having the wiki as a source of documentation for a project can save you goes something like this. Your company completed a project for a client 6 months or so ago. The team member that actually worked on the project got promoted to another job role at another location, or worse yet left the company. Hopefully before that happened that team member wrote down everything you would ever need to get to the source code, login to the production server, or figure out who set the requirements on the client side in the first place. That wiki page may save your butt when the client calls and asks why his web page is displaying an 500 Internal Server error.

3. Searching for information is tedious

How many times have you played out this scenario. A customer asks you about a detail of how business process X works. Let’s say X is the process of buying a product from an online ecommerce site that you maintain.

What’s the first thing you normally do ? Maybe click the help link on the shopping cart page? Maybe you start searching your project folder to find the right solution or project file for Visual Studio to open up and look through some comments for the GetProduct method? Or god forbid, you might open up windows explorer and press F3 to search your network file system looking for a word document or text file that contains the ecommerce API and/or API guide?

searching

If you are like me then you have hundreds of files and/or folders that Windows has to search through and it takes forever, while most of the time not producing the search results you need. And most of the time if you do end up finding the right place the answers within the document don’t help out all that much. This I think is the definition of a tedious process.

One of the most important aspects of a wiki, is that all the content is searchable. Usually if the wiki is a good one, it will have a built in search engine behind the scenes that indexes every word and phrase on every page. That way when you perform a search it is usually as fast as popping open Google and typing in your topic. The search results are normally weighted to get you to the most important wiki page on that search query. Wikis make searching easy.

4. Crowdsourcing is for real

Wikipedia defines crowdsourcing as the act of outsourcing tasks to a large group of people through an open call. With the technologies that the web has brought us over the last few years this has never been truer or easier to do than right now. This principle holds true in any company no matter what it’s size is.

Crowds by Mike PD

Years ago it used to be that there was project manager, technical writer, or gasp, even a developer that was in charge of being the primary documentation creator. And let’s face it, most technical people are not great at writing documentation. Here is where crowdsourcing can really transform the way that knowledge is tracked with a tool such as a wiki. 

As users come onboard to your company they can read and add to any wiki page they want. As each person contributes the document grows and lives with them, and the information just gets better and better. And guess what you don’t have to have a dedicated technical writer to keep that document up to date, you crowdsourced it.

5. Easier to maintain than Office documents

Don’t mistake me, this is not a comparison of Microsoft Office Word to Open Office Writer. I’m not about to go there. What I am referring to is that the type of documents that need a specific program to edit them or share them, does not help you get your job done faster or better. A wiki is a web app that is available to the whole internet or even only your company’s internal network. No special tool is needed other than a browser and a network connection to get to that data, read it, and make a business decision from that knowledge.

It is flat out easier and faster to add or change a piece of information on a topic as your project of client relationship changes via a wiki. This makes it easier to keep all of your business’s information up to date and accurate.

6. Linking related information is crucial

Another important aspects of wikis are how you get from one wiki page to another. This is done using a linking system. Most wikis allow a user to create a link to another wiki page, or even external web page by placing a square bracket around the phrase that they want to link. The following example illustrates this:

To create a link that looks like this:

BizStream will be architecting this project

You would write in the wiki editor this:

[BizStream] will be architecting this project

A link, or a way to jump around from wiki page to wiki page, is as important to a reader or employee as jumping around from link to link on a Google search result page is. Wikis provide an easy way to do this.

A real world example of this is that at BizStream we keep a wiki page on each company that we deal with, and on that company’s wiki page we always include a project section that contains links to all the project wiki pages that we do for that company. It is really nice to be able to navigate quickly from the top level company page to each of it’s projects and see who is leading them, or who is the main customer contact for that project.

It ends up looking like

Company: BizStream

Open Projects

  • BZS-2010-01: Kentico CMS Website Implementation
  • BZS-2010-02: Custom Sharepoint Web Part Integration
  • BZS-2010-03: Database Maintenance Plan Review

7. Built-In document revision and revision control

The commenting and revision system in most word processing tools is overly complicated in my opinion. I have seen enough red lines and red arrows to last for the rest of my lifetime. Also what about when the user forgets to turn tracking changes on, or overwrites the file in the folder. This can become an administrative nightmare.

Normally wikis contain a simple and built-in method of storing all the different revisions of a wiki page. Most good wikis also give you a tool to see the differences between each revision all on a single web page, i.e what was added and removed. Wiki Administrators usually have the power to rollback to a certain revision if something was lost, or if an anonymous user is being malicious and tries to hijack some content. Wikipedia is famous for it’s moderator groups and level of standards they hold to a page and it’s content.

8. Categories / Tags easily organize your content

The popularity of tagging a photo, page, or email has become increasingly useful and popular in almost all things these days. Take FaceBook for example they let you tag almost anything. You can tag each person in a photo, and that tag becomes a link right in that photo, and anyone can click on that person right there and be taken to their profile. It’s just an easy way to organize links or content really.

The reason that tags become useful is that you can narrow down your search results or subscription topics to just what you care about. Even this blog for another example. I have a lot of personal blog posts here, but if you don’t really want to know about them you can just subscribe to the the technical categories or tags and see what you want.

tag 

One tip here though. Don't get too hung up on organizing the content into a very structured layout. This isn't a file system it's meant to be a free flowing system. Just start creating your pages and adding links as you go. The content of the page is way more important than having it look right.

9. The cost is nothing for all the benefits you get

Business is all about using your resources wisely. And what’s wiser than using something that is free. There are many free open source wiki’s you can use. My favorite is ScrewTurn Wiki.

I actually have a lot of reasons why I like and use ScrewTurn as my wiki of choice. But this post has already grown to be a bit bigger than I originally thought so I will explain why next time. So there you have it. My 9 reasons why I think your company needs a wiki.

Please leave any feedback or questions you have in the comments.


I’ve been pretty surprised at the traffic, mention, and email feedback that my last post, 7 Things you might want to check after launching a new Kentico CMS website, has received. In fact I was presented with a very good question in my inbox today about a detail from the fourth item in that list.

Pretty much the question boiled down to, why after submitting the Google site map URL to Google’s Webmaster tools, was the specified website’s pages not showing up in the resulting index or when viewed at the sites ~/CMSPages/GoogleSiteMap.aspx page.

Since the question came in from a friend, I decided to dig deeper and lend a hand. As soon as I logged into the site’s CMSDesk I quickly noticed what was up. Most of the content pages were using Custom Document Types. I was actually impressed to see this because it is sort of an advanced feature to use inside Kentico and normally an under utilized feature as well.

The output of the GoogleSiteMap.aspx page looked something like this, only the normal Menu Items from the CMS Tree:

 

Kentico CMS Google Site Map default

 

So after some quick research I found the devnet article on how to integrate the Googe site map feature. After reading the second paragraph of that article I knew exactly what was up. Turns out that by default the Google site map feature from Kentico only includes the “Page (menu) item” Document Types. This makes sense for someone that doesn’t have a lot of custom Document Types or dynamic content.

However, if you use custom Document Types, and you should, they rock, then you need to pop open your favorite text editor and add a very small amount of code to make the custom pages show up. Open up ~/CMSPages/GoogleSiteMap.aspx. The server tag in question that we are looking to change is the <cms:GoogleSiteMap> tag.

Default:

 

<cms:GoogleSitemap runat="server" ID="googleSitemap" TransformationName="CMS.Root.GoogleSiteMap" CacheMinutes="0" OrderBy="NodeLevel, NodeOrder, NodeName" /> 

 

Now what we need to do is add in whatever custom Document Types that we have, into a ClassNames property, chances are it is not there already. Let's use BizStream.TeamLeaderProfile in this example.

Change To:

 

<cms:GoogleSitemap run at="server" ID="googleSitemap" TransformationName="CMS.Root.GoogleSiteMap" CacheMinutes="30" OrderBy="NodeLevel, NodeOrder, NodeName" MaxRelativeLevel="-1" ClassNames="CMS.MenuItem;BizStream.TeamLeaderProfile" />

 

Don't forget that the property can accept multiple custom Document Types delimited by a semi-colon. Now save the page and you are all set. All of your pages should show up at the GoogleSiteMap.aspx url. Go ahead and browse to it to check it out.

You should now see the extra URLs part of the page output like:

 

Kentico CMS Google Site Map with Custom Document Type

 

I’d also like to point out that the default control does not look to be using the Cache system provided Kentico, CacheMinutes=0. I would recommend upping this value from 0 to at least 30 minutes if not more. There is a small chance that 0 could mean inherit from the top level tree or site, but I haven’t been able to verify this or not.

Note: if your curious about what kinds of pages that a custom Document Type can generate, check out the About page over at BizStream.com. That’s one example of how BizStream uses custom Document Types.

One last tip, like the devnet article mentions you don’t have to use that path to submit to Webmaster tools with, it is kind of long. You can change it to whatever you want by going to CMSSiteManager -> Settings -> (global) in list -> URLs -> Google sitemap URL.

I hope you enjoyed this small article about using the Google site map feature of Kentico CMS. As always leave feedback and opinions in the comments or send them to me email.


(Note: this post assumes that you read the primer post on URL Rewriting & Aliasing in Kentico CMS)

 

In my last post I described what it takes to use the URL Rewriting and Document Aliasing capabilities of Kentico. I also promised a twist to those who made it through the entire blog post, and here it is. So without further ado, I now present my solution for creating a Custom Document Alias in Kentico that is QueryString aware.

Let’s get started. Now that you understand more about URL Rewriting say you had a URL like this:

 

http://mcbeev.com/products/item.aspx?type=Chips&name=Baked Lays

 

Pretend for a minute that your store also sold candy bars, and we decided to reuse the same old page to display both types of products. We would still have a URL like:

 

http://mcbeev.com/products/item.aspx?type=Candy Bars&name=Snickers

 

Our example translated into a standard Kentico setup:

 

url_tree_2

 

And here ladies and gentlemen we hit our first snag with the Kentico CMS out of the box and aliasing. You can URL Rewrite and even Document Alias any node in the content tree. However, the one small gotcha is that you can not create an Alias that looks at QueryString values, or anything after the question mark.

The default Kentico engine will just replace any special character with a dash in the in the URL Path or URL extension field of the Document Alias, therefore wiping out the important part of the QueryString that our example URL above is relying on.

I have actually run into this in my real world projects where I am upgrading a pre existing site that has lots of old links from it’s website as well as other websites that match the URL format in our above example.

Bummer.

To fix this we have to get a little creative and write some code. This is a two part solution. First, we need to make a generic redirector page with a Document Alias of the first part of the URL above.

I created a new page called RedirectItems at the root of my Kentico site and then added a Document Alias like so:

 

item_Redir

 

The purpose of this page will be catch all the incoming requests regardless of QueryString value, and hand it off to the right place after running a bit of code on that page, or Web part.

The next step we need to do is create a new custom Web part. The purpose of this new Web part will be to to grab the information out of the the request that we need. To do so I used these instructions from Kentico on how to create a new custom Web part.

 

WebPart_Redir

 

The key here is that we create the new Web part called Redirector (or whatever you like) and then give it one Property called RedirectToPaths. This is where you will set your URL templates for how we can handle the QueryString redirects.

 

WebPart_Redir_Properties

 

At this point you are safe to click save on the Create Web part Screen. Next we need to add in some code to our Web Part.

Once the new Web Part is added in the CMSSiteManager and the file is in ~/CMSWebParts/Your Category Name/ you can open up the source code of Redirector.ascx.cs with your favorite Text Editor or Visual Studio.  And add the following OnPreRender method.

 

protected override void OnPreRender(EventArgs e)
{
	base.OnPreRender(e);

	if (this.StopProcessing)
	{
		// Do nothing
	}
	else
	{
		//Collect the URL information from the current Request
		string currURL = HttpContext.Current.Request.RawUrl;
		string queryString = string.Empty;
		string newURL = string.Empty;

		//Check to make sure some query string variables exist
		int iqs = currURL.IndexOf('?');

		//QueryString variables exist, put them in a string.
		if (iqs >= 0)
		{
			queryString = (iqs < currURL.Length - 1) ? currURL.Substring(iqs + 1) : string.Empty;
		}

		if(queryString.Length > 0)
		{
			//Parse the querystring		
			NameValueCollection qsKeys = HttpUtility.ParseQueryString(queryString);

			//Pull in the Path Expressions from the Web Part property called RedirectToPaths
			string redirectToPaths = (string) this.GetValue("RedirectToPaths");

			//If the property is empty do nothing
			if(redirectToPaths.Length > 0)
			{

				//Split out the property value by a comma in case there are multiple
				foreach(string p in redirectToPaths.Split(','))
				{
					string path = p;
					bool allKeysInPath = true;

					//Iterate through the keys and replace the key with the value
					foreach (string key in qsKeys.AllKeys)
					{

						if(path.Contains("{"+ key + "}"))
						{
							path = path.Replace("{"+ key + "}", qsKeys[key]);	
						}
						else
						{
							allKeysInPath = false;
						}
					}

					//Handle whitespace
					path = path.Replace(" ", "-");

					//Make sure we got a redirectToPath that handles all the keys
					if(allKeysInPath)
					{
						newURL = path;
						break;
					}

				}

				//Check if we generated a new URL
				if(newURL.Length > 0)
				{
					//Redirect to our new path
					Response.Redirect(newURL);
				}

			}

		}
	}
}

 

 

 

That’s all the code we need to touch, so close and save the file. Now let’s add our new Redirector Web Part to our site. Open up the CMSDesk again and go to where you created the page called RedirectItems from above. Click on the Design tab of the page and add in new Redirector Web Part that we just created. Then click configure. You should be at a modal window that looks like this:

 

WebPart_Redir_Filled

 

Notice our RedirectToPath property shows up and I have typed in two URL templates. Go ahead and copy the second string /Products/{type}/{name} into your Web part. Then click OK. I think you can probably guess how that maps to our original problematic URL of:

 

http://mcbeev.com/products/item.aspx?type=chips&name=Baked Lays

 

Basically the code will replace anything inside the curly brackets that matches the name of the parameter with the value of the parameter. So for the preceding example, we will end up being redirected to the correct location of:

 

http://mcbeev.com/Products/Chips/Baked-Lays

 

So there you have it, this custom Web part will now give us a small enhancement to the built in ability of Document Aliasing for Kentico CMS. The code for the Web part can be found at the link below.

I hope this idea helps you maintain working links when upgrading a pre existing site the way it did for me. As always please feel free to leave feedback for me in the comments.

 

Web part code download


Flow chart of my work

or lack of....


Procrastination Flow Chart

I think it has to be modified to fit my current habbit as well...

  • Play WoW
  • Check Auctions
  • Check InGame Mail
  • Move to parked location
  • Play WoW
  • Screw this dumb quest lets PvP
...

I could go on...

Congratz to Mark and Bonny

They welcomed the first addition to thier family this past weekend. Details here: http://www.markandbonny.com/ We wish them all the best. Also I figure its like having another indentured servant for BizStream, which is good too


What I accomplished in 2007

Things that I did this year, or had some help from MrsMcbeev that got done

  1. Ran 700+ miles
  2. Burned 100,000+ calories running (Thats 28 lbs people!)
  3. Ran a Half Marathon (next year bring on the full!)
  4. Lost close to 30 lbs (too bad some of it came back)
  5. Finished the Avery basement (thanx Charlie, Jay, Mike, Moms and Dads)
  6. Had a beautiful new baby girl
  7. Bought a Minivan
  8. Managed atleast 5 sub contractors who probably make more than I do
  9. Became a legal business partner of the best software company ever, BizStream
  10. Watched our first baby girl get one year older

There might be more but that's my list so far. It's been a good year.


Bike Trip 2007 write up

The Goal: Bike from Grand Rapids, MI to Big Rapids, MI (approx distance 55 miles) along the whitepine trail on Saturday, and back on Sunday.

The Riders: Mark, Scott, Pat, Sterling, Cory, Justin, Michael, Eric, and myself.

What Happened:

We started out at about 9:30 am and surprisingly Justin was not late at all, and our last member showed up just in time to make the start time. The first 15 or so miles north of Grand Rapids is nicely paved and we all crank through it pretty fast. Pat aka "The Spinner" is already way ahead due to his time in the gym on the biking machines he frequents. We make our first stop, right as the paved trail turns into some hard packed dirt. Everyone is in good spirits at this point.

Our next leg leads us through a few small towns such as Cedar Springs, Sand Lake and we struggle a little as the trail gets worse, but make it to Howard City for the appointed half way point and lunch break. The Spinner makes it there ahead of Eric, Scott and myself by about 15 mins and we find the Steelhead bar/restaurant first. The rest of the guys are not too far behind us. The beer and food rocked at there. I highly reccommend it to anyone visiting Howard City. We've went about 26 miles by this point.

After an extended lunch break and I think 4 pitchers of Oberon (really great local brew for those non-michiganders), we saddle up and head out. This is pretty much where hell starts I think. The trail turns to either rock covered or sand. I have a new hatred for sand after this trip. Everyone struggles to keep up. Eric and I ride about 10 miles or so and take a break to wait for everyone. During this, we see a strange man just standing on the side of the trail in the middle of nowhere, a horse and buggy, and maybe 2 other people. That makes it 4 people other than us on this trail so far. We have about 15 miles left now to get to Big Rapids. And they were a killer 15 miles. We had to stop and walk once or twice up some sandy hills. My quads were just burning, and I am out of water by now as is everyone else.

Then the rain started, and it felt really really good for about 10 mins. But then it started coming down so hard you couldnt see and I was soaked to the bone soon after. We finally reached a gas station at about the same time the pavement started for Big Rapids and were very happy to buy some more water. We waited up here for the group and once everyone was in set out to find the Hotel.  We made it there in 4 hours and 44 mins total biking time from Grand Rapids.

Getting to the Hotel was nice, but once we checked in getting in the hot tub and pool was the best feeling ever. I was dead tired and could have fallen asleep in that hot tub real easily.

More eating and drinking ensued after that. Mostly drinking on everyone else's part. I however pretty much crashed and slept through it all. I couldnt even stay awake for the Pistons game, not that I missed much there.

There was a nice breakfast spread on Sunday morning. That tasted mighty fine. 9:00 am hit and everyone minus the Cory and Sterling was ready in the lobby to take off for home.

The return trip did not start out well. We went about 1 mile and stopped to buy some water. That is where the problems started for me. My water bottle inside my pack leaked and everything was soaked. Then my bike fell over and the handle bars bent a little. Another mile and my luggage rack broke off the frame and we had to stop and rig the thing back together with some Zip Ties (best invention ever) and bungy cords. I think the rain was starting up again now, but just a sprinkle. Another mile and Pat's tire blew. Luckily it would hold some air and he could pump it up and go. So yeah the first 4 miles or so I think we stopped 4 times. That was not a good pace.

Then the rain started for real, and did not stop the entire trip home, except for maybe 10 mins or so. We stuck to the paved roads on the way home, because the trail sucked dry, but wet it was worse. The problem with paved roads was that was almost no shoulder. A couple trucks got close to us which really sucked. We made it to Howard City again and stopped for lunch without much else happening. The food once again rocked. But we were all soaked and got cold sitting just sitting there.

Starting back up after lunch really sucked. It was really really cold, because of the wind and wetness. But evert so slowly we made it back. It was really really nice hitting the paved trail again on the south end of Cedar Springs. There was a slight downhill grade and Eric Mark and I probably averaged a 17 mph speed the last 15 miles. We made it to the car at about 3 hours and 55 mins. You can see the big difference between the trail and paved roads.

All and all it was a great time. I have never biked that far, and now I can say that I have.

Total Trip miles: 102 (approx could be closer to 110)

Total Trip Time : 8 hours, 39 mins

Total times we cursed The Spinner out and swore he was juiced: alot

Amount of training miles a rider must complete next year before being invited: 60

 


BizStream in the News

http://video.woodtv.com/index.php?video id=5471

If you watch reaaaaal close there is a 10 secs spot in the firehouse with one of our associates getting interviewed by woodtv. Nice.

It's still really cold out for the record. 

I've released some new features lately at www.workoutdump.com. You should probablly go check them out now.

These are the Wii games I now own. Wii Sports is still the most fun for almost anyone to play. Mrs Mcbeev is getting too good at tennis. She can pretty much beat me whenever we play. Zelda is finally getting good after I got through the first town of learning how to play. Just give me a sword darn you nintendo! I dont need your tutorials. The control of the sword is pretty darn sweet. You slash and swipe at the air with the wiimote and Link mimics it in his virtual world. You can also fish any water, and again you use a real fishing motion to catch fish.

I bought another nunchuk and Excite Truck this morning on our quest for Wii's. We got a Wii for every member of BizStream this morning. It only took about 4 or 5 phone calls and 2 or 3 long lines, but our local Best Buy had 36 boxes, and we got 21 - 22, 25, and 27. It definitely would have sucked to be number 37. Excite Truck is very fast and furious. You drive by holding the wiimote horizontal and using it like a steering wheel. Driving games are fun very fun.

If anyone else is watching the Colts and Pats battle it out right now they are lucky. This is a very good game. Much better than that dominating win by the Bears. Im happy the Bears won dont get me wrong, but it was a boring game to watch towards the end. I think a Bears vs. Colts superbowl would be nice. I don't want this game to end. Once it does that means basically football is done for the season, outside of the superbowl. What will we watch in it's place?

Team Building at it's finest

BizStream rocks that's all I can say about today. We all had a great road trip, and had our wallets get a little lighter, but it was totally worth it.

In other news the Holiday season just hasnt been the same without snow, this 40 degree weather is creeping me out. I need a white Christmas! There is however, one more change. We have one more party tomorrow. Maybe there will be 2 ft of snow when I wake up. Maybe...maybe not.

I know that President Ford was a great President for our country, and he was probably one of the best people ever to come out of Grand Rapids, but dang it, this funeral stuff is getting out of hand. They are talking about 10,000 cars expected to need parking, closing down the highways, and a gazillion people lining the streets for the 2 different funerals on tues and wed of next week. I do not want to be out in that crap.

Everyone have a safe New Year's!

About Me

I have over 11 years of experience doing this crazy web development thing mainly at BizStream. I'm passionate about software platforms and technologies that can help solve real world problems. I enjoy hanging out with my wife, chasing around my three children and vigorously rooting for the Michigan State Spartans and Detroit Lions. (I know, right? Who still roots for the Lions?)

View Brian McKeiver's profile on LinkedIn

 

PhotoStream

Calendar

<<  September 2010  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar

last.fm

Kings of LeonCloserabout 26 days ago
.38 SpecialFantasy Girlabout 26 days ago
Dirty Pretty ThingsGin & Milkabout 26 days ago
Little Man TateThis Must Be Loveabout 26 days ago
MilburnBrewsterabout 26 days ago
The HollowaysFit For A Fortnightabout 26 days ago
Kings of LeonI Want Youabout 26 days ago
Hard-FiSuburban Knightsabout 26 days ago
AerosmithJust Push Playabout 26 days ago
DispatchMissionabout 26 days ago

Xbox GamerCard

Offline 9/6/2010 9:47:26 PM Last seen 09/07/10 playing Xbox 360 Dashboard