Home »

Togo: An Ektron Context Engine

  1. Author
    Who built it.
  2. License
    How you can use it.
  3. Warranty
    Hint: there is none.
  4. A Word of Warning:
    What you agree to.
  5. The Problem Togo Attempts to Fix
    The "big idea" -- if you're looking to cut to the chase, start here.
  6. How it Works
    Code samples and a behind-the-scenes explanation of what it's doing.
  7. Download
    The actual code, and MSDN-ish documentation.
  8. To Do:
    Where it's going.

Author

Togo was developed by:

Deane Barker
Blend Interactive
deane@blendinteractive.com
http://blendinteractive.com

License

Togo is public domain software.  It is free to do with as you wish, including using it in other commercial software.

I had originally considered making it open-source under the General Public License (GPL), but my fervent hope is that this theory (or actual code) will someday make it into the official Ektron product, so I didn't want to apply any licensing that might impede that.

However, if you make change or improvements and you'd like to see them make it into the "official" codebase, send me your changes.

Warranty

There is none.  Togo is absolutely "use at your own risk" software.  We've had great luck with it, but we provide zero warranty.  For all you know, it might melt your server and steal your children.  It's a risk you need to be willing to take.

Two other notes regarding Togo and Ektron, the company:

A Word of Warning

Togo is a hack.  It reads directly from the Ektron database.  When instantiated, the EktronContext object makes direct database calls to form its XML document.

Note: all database access is read-only. Togo makes no changes to data.

This may or may not make your nervous.  Togo has been tested against Ektron versions from 5.x through 7.5.1 and has performed fine.  Additionally, the columns in the tables it talks to ("content" and "content_folder_tbl") have been stable since at least version 5.x.  Logically speaking, they will likely stay stable for many future versions, as they're base functionality in the Ektron system.

But, there are no promises.  Togo needs to be regression-tested with each new Ektron release.  You have been warned.

The Problem Togo Attempts to Fix

One of the biggest problems I’ve had working with Ektron is context. Content blocks don’t live in isolation. Just as important as what’s in the content block is where the content block is located relative to other content.

There are settings that need to have values based on a content block’s position in the tree. These things range from what menus to use to what color the header should be. This is true of most every Web site I’ve done beyond the trivially simple.

To address this problem of context, Togo allows you to define custom data structures at any point in the Ektron content tree, then retrieve that information when rendering content.  When retrieving these structures, you can define a point in the content tree, then "look upward" toward the root of the tree, and return the settings "closest" to that point.  This allows you to define settings in the tree, then override them further down the tree, knowing that anything below that point in the tree, will use the settings closest to it.  This allows templates to be "spatially aware" of where their content is in the content tree, and use different settings based on that location.

Example

Your Web site has a main menu, but subsections of the site have their own menus.  You can specify the ID of the main menu at the root of the content tree.  Any content below it will use that Menu ID.  Further down the tree -- perhaps in a "Human Resources" folder -- you can specify a different Menu ID.  Content below this point will use that menu, rather than the root menu, simply by virtue of being in placed in that folder.

So, to create a subsection of the site serviced by a particular menu, you simply specify that menu ID at a specific folder level.  Content for that menu should be placed in that folder or below it.

Togo does this by having you to define settings as SmartForm-based content, then having place content based on those SmartForms are selected places in the content tree.  Your templates can retrieve those settings based on their location in the tree, and their location relative to the content they're rendering.

Togo does this by returning all the content found in specifically-named folders.  When provided with a starting point, Togo will move from that folder back to the root folder, and combine all the settings content it finds into a single XML document, which you can then query for information about where your content is in the big picture, and what settings have be defined above it.

How it Works

1. Create Settings

Settings content objects are comprised of simple SmartForms.  You can design them however you design other SmartForms.

Assume we want to define banners for our Web site.  Specifically, we want to bind banners to folders -- all content in Folder A uses a certain banner.  All content in Folder B uses a different banner.  And all content in Folder C -- which happens to be a subfolder of Folder B -- uses yet a different banner.

Our banners consist of (1) an image, (2) some text that gets overlaid on top of the image, and (3) a URL that the image links to.  We create a SmartForm to represent this, and the resulting XML looks like this:

<root>
	<BannerSettings> 
		<Text/> 
		<Image/> 
		<Url/> 
	</BannerSettings>
</root>

The "root" element is standard to Ektron.  The "BannerSettings" element is a Group box placed around the other elements.  You'll see below why this is handy, and I've found with Ektron in general that it's a good idea to "namespace" your XML like this, so that you can always identify a unique XPath pattern for every different SmartForm (with the above format, you could get "Image" elements from Banner Settings objects based purely on XPath, since the "BannerSettings" element is embedded in the document).

2. Place These Settings in the Folder Tree

Once you have these SmartForms created, you can create content based on them inside "settings folders."  A settings folder is any folder named "_settings" anywhere in the content structure.

These settings will be available to any of the content at the same level or below.

Picture it like this: any folder called "_settings" contains the settings for its parent folder.

3. Retrieve These Settings in Your Template

In your .Net Web form, Togo consists of a single class file, to be placed in your App_Code directory.  To create the context, you simply instantiate the object and pass it a folder ID.

//Assume we want contextual information from folder ID 47
EktronContext ThisContext = new EktronContext(47);

You can also use the folder ID found on a ContentBlock control:

//Assume a populated ContentBlock server control called "MyContentBlock"
EktronContext ThisContext = new EktronContext(MyContentBlock.EkItem.FolderId);

Important: for the purposes of this documentation, the folder passed into the constructor is henceforth known as the "source folder."

This object now contains an XML document containing information on all the folders from your source folder upwards to the root folder.  This information includes the name, ID, and description of the folder. plus all information contained in any "_settings" folders it finds.

Assume for a moment, this folder structure:

If we asked for the context of the "Customer Service" folder, the XML document returned would look something like this:

<Context>
	<Folder id="47">
		<Name>Customer Service</Name>
		<Description>The customer service department</Description>
	</Folder>
	<Folder id="13">
		<Name>Departments</Name>
		<Description>Container for all the departments in the company</Description>
	</Folder>
	<Folder id="0">
		<Name>Root</Name>
		<Description/>
	</Folder>
</Context>

This document represents the path from the source folder back to the root folder.

Now, assume we place a "_settings" folder under the root, and in it we place a Banner Settings object.  We also do the same in "My Folder," further down the tree.  Like this:

Now our XML looks like this -- note the new "Object" elements within the respective "Folder" elements.

<Context>
	<Folder id="47">
		<Name>Customer Service</Name>
		<Description>The customer service department</Description>
		<Object id="383">
			<BannerSettings>
				<Text>My Folder's Banner</Text>
				<Image><img src="/some/url/to/my/banner"></Image>
				<Url>http://somewheretogo.com</Url>
			</BannerSettings>
		</Object>
	</Folder>
	<Folder id="13">
		<Name>Departments</Name>
		<Description>Container for all the departments in the company</Description>
	</Folder>
	<Folder id="0">
		<Name>Root</Name>
		<Description/>
		<Object id="136">
			<BannerSettings>
				<Text>The Root Folder's Banner</Text>
				<Image><img src="/some/url/to/my/banner"></Image>
				<Url>http://somewheretogo.com</Url>
			</BannerSettings>
		</Object>
	</Folder>
</Context>

The idea -- the very intention of Togo -- is that this XML represents the context of the source folder.  It's a representation of the location this folder occupies in the Ektron content universe.  By using this XML, you can ask questions about your source folder's (and, by extension, your content's) position in the big picture.

Some things to note:

Once the EktronContext object is created, you can simply run XPath on the XML document to retrieve context information, if you like.

//Let's check if our immediate folder has a banner
if(EktronContext.XmlDoc.SelectSingleNode("/Context/Folder[0]//BannerSettings") != null)
{
	//Do something
}

There are also several convenient methods on the object to allow you to retrieve common information. 

a. GetNearest

The most-used is "GetNearest," which, when passed some abbreviated XPath, will return the first matching node -- this represents the contents of that setting nearest the folder.

//Extract the banner information nearest to my folder
string BannerText = ThisContext.GetNearest("BannerSettings/Text");
string BannerImageUrl = ThisContext.GetNearest("BannerSettings/Image/img/@src");
string BannerNavigateUrl = ThisContext.GetNearest("BannerSettings/Url");

Note the XPath that is passed into the method. It's abbreviated -- "/Context/Folder/Object/" is assumed and is automatically pre-pended to the passed-in argument.

To complete our banner example, say we have this code in our Web form:

<asp:Hyperlink id="BannerLink" runat="server">
	<asp:Image id="BannerImage" runat="server"/>
	<asp:Literal id="BannerText" runat="server"/>
</asp:Hyperlink>

It becomes fairly simple to ensure these server controls are always populated with the correct information based on the position of our content in the folder tree:

//Get the context for this content
EktronContext ThisContext = new EktronContext(MyContentBlock.EkItem.FolderId);

//Extract the banner information nearest to my folder
BannerText.Text = ThisContext.GetNearest("BannerSettings/Text");
BannerLink.ImageUrl = ThisContext.GetNearest("BannerSettings/Image/img/@src");
BannerImage.NavigateUrl = ThisContext.GetNearest("BannerSettings/Url");

The the above code will populate our banner with the nearest Banner Settings object it finds, starting at the source folder and working its way back to root.

b. IsUnderFolder

IsUnderFolder will accept either a folder ID or name as the target folder, and will return a boolean indicating whether or not the source folder is under the target folder.

Assume we want to handle all content in the Departments branch of the folder tree differently.

//Get the context of our folder
EktronContext ThisContext = new EktronContext(47);

//Determine if it's somewhere in the "Departments" branch of the folder tree
if(ThisContext.IsUnderFolder("Departments"))
{
    //Do something
}

Obviously, using the Folder ID is more precise.  Folder IDs are unique, but there could be more than one folder named "Departments."  When used with the folder name, IsUnderFolder just determines whether or not there's a folder with that name somewhere in the parentage of the source folder, it can't differentiate between folders with the same name.

c. DistanceFromFolder

DistanceFromFolder will accept either a folder ID or name as the target folder, and will return an integer representing how far the source folder is from the target folder.  For example:

Return Value Meaning
-1 The source folder is not under the target folder.  This is the same as a "false" result from IsUnderFolder.
0 The source folder is the target folder.
1 The source folder is an immediate child of the target folder.
2+ The source folder is this many levels removed from the target folder.

For example, to determine if your folder is a child of any department folder, you could find out if it's a grandchild of the Departments folder.

//Get the context of our folder
EktronContext ThisContext = new EktronContext(47);

//Determine if it's a child or a child of the "Departments" folder
if(ThisContext.DistanceFromFolder("Departments") >= 2)
{
    //Do something
}

The same caveats regarding using Folder ID vs. Folder Name apply equally to this method.

Download

Togo consists of a single class file. Place this in your App_Code directory. Alternately, you can compile it and put the assembly in your bin directory.

To Do