Articles in this series
Part 1: The requirements
Part 2: Installing Sitecore and setting up a Visual Studio solution
Part 3: Architecture
Part 4: The basic layouts
Part 5: Identity and documents
Part 6: Navigation
Part 7: The homepage - content oriented spots
Part 8: The homepage - presentation oriented spots
Part 9: News
Part 10: Search
Part 11: Deploying the solution
Requirements for the homepage
A frontpage on a site rarely holds content itself, but pulls out teasers and content from other pages. This is what we are going to implement. This means that we are going to have several small individual presentations, which the page consists of. These small individual presentations we are going to refer to as content spots; some people refer to them as widgets or web parts.
There are going to be several different types of content spots and these should, as with everything else, be grouped by function and not by type. This means that the individual content spots should be placed in whatever component they are implementing functionality for. For instance the news spot should be placed in the news component.
However there are some generic content spots, which isn’t associated with one component. This is the case for the image spot and the HTML spot, which is described in the first part of this series. These are generic and should therefore be implemented in the spot component.
As you might have noticed the functionality of the frontpage revolves around content spots. As this functionality is general, there is no reason to constrain it to the homepage. Therefore we are not going to create a homepage component, but a spots component. In that way it can be used on different page types, which has nothing to do with the homepage.
Several ways to implement
The functionality can be implemented in many different ways, and they have different advantages, so it is up to you to decide what benefits you are going for. In this article we’ll go through the first example of implementing it, while we will implement the other in the next article in this series:
- In the first implementation we are not going to utilize the Page Designer, but assign spots to a page using normal Sitecore content fields. This implementation may lack the usability of the page designer, but wins in maintainability and enables editors to audit sites without handing over responsibility of the presentations from the developer to the editor.
This implementation will be focused around content. The idea is that the editors don’t need to focus on how content is presented. Instead they can focus completely on content.
- In the second implementation we are going to utilize the Sitecore Page Designer, which is often the way Sitecore suggests. In this implementation you are going to give control over the presentations to the editors and let them assign different sublayouts directly to the item. This has its disadvantages as well. First of all until Sitecore 6.4 this implementation will result in layouts are going to be copied from standard values to the content item. You can read about that on my and Thomas Eldbloms blog, which states that it is a bad idea to assign layouts to items.
The content focused implementation
In the content focused implementation we will focus our solution on enabling the editors to create global spots, which can be reused throughout the site. The editor will start by creating a spot in a global folder and then assign them on the frontpage or any other page using the spot framework – this is done through a multiselect field.
The content spot functionality can be divided into two parts, each responsible for each there area:
- The content spots and the presentation they need.
- A spot presenter, which can present a list of spots, which it knows nothing about.
By dividing the functionality like this, it is possible to separate the solution by function and not by type as discussed in this post. In that way a spot presenting a list of news, can be developed in the news component and then the spot presenter can render it, without knowing the news component. This is done by defining how a spot is presented in the template representing the content spot, which will be described later in this article.
When functionality is generic and not relevant for any specific component, it must be grouped with a component based on type. Therefore the more generic spots will be defined in the spot component itself. This is relevant for the HTML spot and the image spot, which is described in the first article. These spots are not related to any component by its function, but are generic content spots and are therefore placed in the spot component.
The content spots
To create these spots, we start out by defining the templates. The HTML spot consists of a title, text, a link and link text which will be shown in the bottom of the spot. The spot will look a lot like one of the spots on the homepage of Learn Sitecore except it doesn’t contain an image.
To start off with lets create the HTML spot first. As mentioned we need to enable the editor to specify a title, text and a link including the link text. So we need to create a template which allows this. Create it in a component folder called Spot:
Now we need to create the presentation using the same procedure as in the other articles. In this case we are going to use XSLT, but if you are using sublayouts the concepts are the same. Create the XSLT in a Spot folder (both in Sitecore and in the filestructure). Add the XSLT to a Spot component Visual Studio project (as shown in the earlier articles).
Now we need to develop the functionality. First start out with rendering the title and the text. This can be done using the Sitecore XSLT controls sc:text and sc:html. After that we need to render out the link. This can be done by using the Sitecore XSLT controls sc:text and sc:link, which should give you something like this:
< xsl:template match = "*" mode="main">
< div class = "Spot">
< div class = "HTMLSpot">
< div class = "SpotTitle">
< sc:text field = "Spot_Title"/>
</ div >
< div class = "SpotText">
< sc:html field = "Spot_Text"/>
</ div >
< div class = "SpotLink">
< sc:link field = "Spot_Link">
< sc:text field = "Spot_LinkText"/>
</ sc:link >
</ div >
</ div >
</ div >
</ xsl:template >
The text and HTML controls simply outputs the text or HTML specified in the referenced field. The link controls takes in a link field and then generated a link to the linked item using the text in between the tags.
Right now you might be wondering why we don’t specify an item where the fields are actual present as this presentation is going to be rendered on the frontpage, but rest assure we will return to this. For the moment you can pretend that the presentation is used directly on the item containing the content.
Now that we are done with the HTML spot, we can do the same for the Image spot, which should hold an image field and a link, which the user will be directed to when clicking the image. So go ahead and create a template for the image spot:
Now go ahead and create the presentation part as well. In this case we stick to the XSLT. I create it in the spot component and include it in Visual Studio, so that I can develop on it. The rendering itself is fairly simple and it just renders the image using the Sitecore XSLT control sc:image and sc:link which we used in the HTML spot. This should make your code look something like this:
< div class = "Spot">
< div class = "ImageSpot">
< sc:link field = "Spot_Link">
< sc:image field = "Spot_Image"/>
</ sc:link >
</ div >
</ div >
Creating the user interface for the editor
Now we need to enable the content authors to create spots. As this is a simple information site and as I presume that there is going to be a limited amount of spots, we convinced the client just to have a simple and one leveled structure, where they can create spots globally. Therefore we need to create a folder, where content authors can create the content spots. This means we need to create a spot folder template, so that we can specify the insert options, so that the editors can create new spots easily and intuitive. The spot folder doesn’t actually belong in the spot component, as this is more of an organizing template structuring content together like the GlobalNavigationFolder, which you can read about in this earlier part of the series. So go ahead and create the spot folder template in the PageTypes component, specify an icon and then create standard values by selecting the template, clicking options and then Standard values:
Now an item under the spot folder is created called standard values. Standard values are sort of fallback value for a field. In that way you can add a field value to the standard value and all items based on the spot folder template will by default be using the values specified on the standard values. This makes it a really good option for defining initial field values, which is used if the specific field isn’t edited on the specific item. In that way an item will return the value from the standard values item, if the field isn’t edited. Read more about standard values in Sitecore here.
Now add the Image spot and the HTML spot to the insert options field on the standard values:
If you now add an item to the content structure based on the spot folder template, you will see that the editor easy can create spots. Create a few example spots:
The spots created in the global spot folder can be regarded as a repository, wherefrom the editor can select a subset of spots, which will be shown on the specific page. This means we should somehow enable the content editors of selecting spots. This can be done by creating one or more multiselect fields with the datasource set so that it will be possible to select only spots. In the frontpage we want to be able to present two columns of spots, which means we have to create a template with two multilists – one for each column. This template should be created in the spots component:
Now that we have created the template, we need to set the datasource on each field, so that the editor will only be able to select items that are spots. This can be done in several ways.
We could decide to hardcode the path to the spot folder, but this would give some issues, if we decided to create a copy of the site, for a white label instance or similar.
Another option would have to create a query that looks for the ascendants of the current node, looking for a website root template and then find the spot folder below. However this this would create a dependency from the spot component to the pagetypes component. If we look at the design of the solution, this would create a circular reference between the components, which should be avoided.
Therefore we should find a way to create a datasource, that finds the spots, but doesn’t create any dependencies on other components. The problem is that the Sitecore Query syntax currently doesn’t support any functionality to query on inherited templates. This means we cannot let the root or folder inherit from a base template and then query on this. This makes it quite hard to set the datasource. What we normally do in Pentia is making our own custom multilist field, which can find the root based on inherited templates. But this is beyond this article and we are therefore going to take a shortcut. We are going to base the query on names.
Notice that the query is: ancestor::*[@@name='Home']/*[@@name='Spots']/*
Sitecore Query works very similar to XPath but have a little different syntax. You can read more about Sitecore Query here: http://sdn.sitecore.net/Reference/Using%20Sitecore%20Query.aspx
Our query looks through the ancestors of the current item and then finds a node named Home and then a node named Spots. This isn’t all that nice and you should definitely ensure that your editors can’t rename the home and spots node, but the solution will be sufficient for this small example.
Creating the spot presenter
Now that we have enabled the editor to select the spots, which should be presented and created the presentations and templates for the spots, we need to create a presentation that takes the selected spots, and renders their presentation in two columns on the frontpage. So go ahead and create a sublayout called TwoColumnSpots:
This sublayout will be responsible for creating the grid for the content region on the frontpage, but will not contain the functionality to render the spots itself. The functionality will be put into a more generic usercontrol, which can be used in other presentations as well. For instance if we want three columns, we might as well reuse the presentation to render the spots. This makes the TwoColumnSpot sublayout rather simple:
< div class="TwoColumn">
< div class="LeftColumn">
<!-- put generic spotrender here -->
</ div >
< div class="RightColumn">
<!-- put generic spotrender here -->
</ div >
</ div >
Now we need to build some sort of control to render the spots. We can create a generic usercontrol to do this, which takes the name of the field, where the editor has selected the spots, as a parameter. There is no reason for this control to be a Sitecore sublayout, as we want to use it in a sublayout grid, so go ahead and create a new class in Visual Studio called SpotPresenter and make it inherit from a UserControl:
To be able to set which field the spotrenderer should select spots from, we need to create a property where we can set the field name:
public string SpotFieldName { get; set; }
We want the control to render the spots, when it is loaded, so we want to override the Render method:
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
//Call a method which will render the spots
RenderSpots(writer);
}
private void RenderSpots(HtmlTextWriter writer)
{
throw new NotImplementedException();
}
The RenderSpots method needs to find the current item and then locate the field, where the spots are selected:
//Get the current item from Sitecore
Item currentItem = Sitecore.Context.Item;
if (currentItem == null)
throw new InvalidOperationException("Context item doesn't exist. Is the presentation run outside of a Sitecore context?");
//Get the spotField to render
MultilistField spotField = currentItem.Fields[SpotFieldName];
if (spotField == null)
throw new InvalidOperationException("Cannot find any spotfield with the name " + SpotFieldName ?? "null");
We then need to get the items, which are selected in the field, which can be done using LINQ. Then we need to iterate over these and render the presentation of the spot:
//Use LINQ to select the target Items
IEnumerable <Item> spotItems = spotField.TargetIDs.Select(id => Sitecore.Context.Database.GetItem(id));
Now we have all the spots that need to be rendered. However we haven’t defined how a content spot, should be rendered. That is, we don’t know which presentation we should render the content with. So we need to define a way for the content in Sitecore to point at a presentation. This can be done in several ways, but we might as well do it in similar way to how Sitecore defines how content should be presented – through the normal layout field. So go to the standard values of our two spots and set which presentation should be used:
You will see that there is selected the Sample Layout. This is due to the fact, that Sitecore doesn’t allow renderings and sublayouts to be added to the layout field without the presence of a layout. Seeing as the content shouldn’t be presented itself, I have selected the sample layout, but any layout would do and if you want to goldplate the solution create an empty layout, which throws a 404 page not found, as this will make users unable to browse to the spot and get something presented.
Do the same for the HTML spot.
Now we have defined how a spot is presented. This means we can now render the spots, by iterating over them a call a method responsible for extracting the presentations and render them to the page:
//Iterate over each spot and call a method to render it
foreach (Item spotItem in spotItems)
RenderSpot(spotItem, writer);
The RenderSpot method will then look like this:
private void RenderSpot(Item spotItem, HtmlTextWriter writer)
{
//Get the current Device
DeviceItem device = Sitecore.Context.Device;
if (device == null)
throw new InvalidOperationException("Cannot find the context device. Is the presentation run outside of a Sitecore context?");
//Get the selected renderings on the spot item
RenderingReference[] renderings = spotItem.Visualization.GetRenderings(device, false);
foreach (RenderingReference reference in renderings)
{
Control control = reference.GetControl();
//Set the datasource for the presentation
((Sitecore.Web.UI.WebControl)control).DataSource = spotItem.Paths.FullPath;
//use the Sitecore API to render out the contents of the presentation
control.RenderControl(writer);
}
}
Here we get the presentations set on the spot for the current device and then we set the datasource to the spot item and then call the Sitecore API method RenderControl, which renders the contents of the presentation. Notice that we actively set the datasource and that is why the spot presentations doesn't use the context item as its datasource.
Now all we need to do is add the control to the TwoColumnSpot sublayout:
<% @ Control Language="C#" AutoEventWireup="true" CodeBehind="TwoColumnSpot.ascx.cs" Inherits="Spot.TwoColumnSpot" %>
<% @ Register TagPrefix="spot" Namespace="Spot" Assembly="Spot" %>
< div class="TwoColumn">
< div class="LeftColumn">
< spot : SpotPresenter SpotFieldName="Spot_LeftSideSpots" runat="server" ID="LeftColumnSpots" />
</ div >
< div class="RightColumn">
< spot : SpotPresenter SpotFieldName="Spot_RightSideSpots" runat="server" ID="RightColumnSpots" />
</ div >
</ div >
Here we have registered the tagprefix Spot and added the controls, where we specify the property SpotFieldName. Now add this presentation to the FrontPage template:
Select some spots in the spot fields on the frontpage, publish, compile and then hit the frontend. With a little styling you should now see something like this, where I have selected the HTML spot in the left column and the image spot in the right column:
Now you are done. In the next part we will look at how the same functionality can be achieved using the unified page editor in Sitecore 6.4. Also we will discuss advantages and disadvantages for the two kinds of implementations.