Interface Templates

14. december 2010 by Thomas Stern

For this post I will look into a simple technique for building easy to extend or reuse features in Sitecore solutions. This simple architectural concept will, for the rest of this blog post, be refereed to as an Interface template. This idea comes from my work in Pentia, where we build website that should be easy to maintain and reuse or extend implemented features. Some of you, that where at Dreamcore 2010, might have been so lucky, that they saw my brilliant colleague Thomas Eldblom in cooperation with Dan Algrove from Hedgehog development, in a session on how to build maintainable Sitecore solutions. You can read more great thoughts from Thomas at his blog Molten Core. Some of the key points from this session and my daily work with Thomas is the base for the blog post, and I will not take any credit for being the inventor for the interface template concept. I've just used it a lot recently to make it easier for new templates/pagetypes to reuse implemented code features, and thought I would make for a good post.

 

We all know that code degrades as time goes by, and that the more "quick fixes - Hey it works don't change it" where used during implementation the faster the code degrades, and oh yes even the best code will degrade over time, but maybe not on a quite as steep slope.

 

We have all tried to write simple parts of a website, but even the simple parts may lead to complicated code. Let's take an simple example as a Menu or Breadcrumb path. Most websites have some sorts of navigation and likewise have breadcrumbs. So to start with the Customer have a simple website where the breadcrumb starts at the current item and works it's way up the content tree to the root of the site i.e. at the front page. It would be an fairly easy task to implement code that can build this sort of breadcrumb path. This could be done either in an simple xslt or maybe by implementing some sort of breadcrumb provider in c# class.We will traverse every ancestor until the following condition is meet. This could for example be done using a Sitecore query a look something like this.
Note only pseudo code will be used in this post.

 

ancestor-or-self::item[@tid = 'frontpage']


This will work great and is a simple and acceptable solution. So what is the problem ? After an year your customer returns with an wish that they want functionality restart the navigation "breadcrumb" for some node in content tree, this could be anywhere. So the root item ie the first link in the breadcrumbs path is no longer the front page but could potentially be any node.
The question now is, how do you determine where the root of the breadcrumbs path is? You can no longer settle for a simple test against the "frontpage"-template or could we ? A simple way would either be to make an new front page at the given point in the content tree, but it seems stupid that the customer will have to use a front page for this task and might be confusing as well. So okay no problem we will just make an new template, or choose a page type and add the template to the query condition and it might look something like this.

 


ancestor-or-self;;item[@tid = 'frontpage' or @tid = 'subfrontpage']


Great it works and was pretty simple too, but now we started on the slippery slope.The key point to notice here is that for this to work we had to make changes to the code.
What will happen the next time the customer returns with a wish to reset the navigation for the all ready existing document template or any template for that matter. And even more the document template should have a check box that enables the reset features. No problem we simply add the check box to the document template and extend the select query.

 

ancestor-or-self::item[@tid = 'frontpage' or @tid = 'subfrontpage' or (@tid = 'document' and resetmenuField = '1' )]


Wow it worked but now the query is becoming quite complex and keep in mind this is an simple example. And soon the customer will be back with a note that the shoppingItemTemplate needs to reset the breadcrumbs as well and with the same condition as the document template. As the years go bye the query is becoming more and more unreadable.

 

But how can we avoid this slippery slope ?
We could build a new template and let it inherit from the "frontpage"-template, new template will then have the same fields as the front page. This might lead to a more confused customer because a simple document template will have the same field as front page without using them.

So now we will have a look at an interface template. This could be a simple template with or without fields. The sole purpose of the this interface template, is to define "some functionality"
for another templates that inherits from it. Lets revisit our example with the breadcrumbs.
First we define this template without any fields lets call it _isMenuRoot. And let the frontpage template inherit from this. Now we can write the first query as this.

 

ancestor-or-self::item[ is derived from template _isMenuRoot]


The query will return the front page as the menu root just as we wanted. Perfect now the beauty of the simple template comes into play. Now the customer wants a simple sub front page that can reset the breadcrumbs. This should be a fairly easy task. Build your normal sub front page a let it inherit from the _isMenuRoot template now look at the query


ancestor-or-self::item[ is derived from template _isMenuRoot]

Wow it's the same and we didn't have to change anything in the code, great!
Now let's extend the template so it has a simple checkbox field that allows back end users to enable/disable the reset functionality. This is done simply by extending the _isMenuRoot Template with an checkbox field. Of course this demands for a simple change to the code, so we change the query accordingly so it reads.


item[ is derived from template _menuRoot and reset menu = 1]


Of course this will force back end users to actively enable/disable this feature for existing items. But this can be done fairly easy on templates that inherit from the interface template. For example, you might want the front page to always have this feature enabled, and as default disabled for a document page or alike. You can achieve this but setting the checkbox value in standard values for all the templates the inherits from our _isMenuRoot interface template. Thank you sitecore for standard values. So without changing any code, future templates can nowgain access to this Menu root functionality by inheriting from our _isMenuRoot template.

 

The above helped me on project i recently worked on where I had an if statements that had to test for 8 different templates. Everytime someone came to develop new templates/page types they had to go in and actively add another clause to the if statement, if they wanted to acces the code features provided after in if statement. This were change by the use of an interface template so the if statement checked for an interface template instead of 8 different templates .And of course all the template previously in the if statement, now simply had to inherit from the newly created interface template. New templates/pages, that want to reuse the same functionality, simply had to inherit from the interface template,without the need for changing any code at all.

 

 

Usersessions in sitecore (logout users from backend)

7. juni 2010 by Thomas Stern

 

The number of usersession that is possible to have open in sitecore depend onj the licens, which is fair enough. But the task for an administrator to end hanging usersession seems somewhat headless. The administrator have to logout, to get a list of active user sessions, and then choose to end hanging or not used sessions. This might be fine in minor solutions but I think this might pose a problem in solutions where a user with the "Is Adminstrator" flag is set to true can be hard to find. He might be working in different office or just generally be hard to get a hold of. It doesn't require you have the "Is administrator" flag to end session but it is required to get the list of active sessions. You don't even have to be logged in to sitecore to end active session, if presented with the option to end active session anonymous users could do so. My idea is to help local administrators with functionality to end session with out leaving the backend, or having to log out or anything like that. Since sitecore is one big tool box I will build it as shortcut in the tray of sitecore.
Okay so we start, all our work is done in the core database and it only contains minimal coding. We start with making the tray icon. In the core database we go to content/applications/Desktop/tray and add a item I will call it UserSessions the Data section of the item find the click attribute and add the command name, this will also have to go in the command file we get back to that. I also and icon for the tray I have choosen the standard user icon.


userhelper1


Now with the tray icon shortcut in place you should be able to see it in th start bar just save the item and hit F5

 

userhelper2

Now we will add the command to the App_Config/Commands.config add the following line

<command name="Tray:UserSessionViewer" type="UserHelper.UserhelperTrayCommand,UserHelper" />


I will add this little user feature helper as an application so we need to add an application and layout for the application in the core database. We start with adding the layout "core database" go to sitecore/layout/applications and add a new layout in the data section in path write the physical path to layout you will create in the next step.

 

suerhelper3


Next we will add the application again this is done in the core database go to sitecore/content/Applications/ and add a new application pull on the the layout we just created make sure you can see it in the layout section for the item. Also note the Id for your application, we will have to use it in the code part.


userhelper4


Okay now we are done with the configuration in sitecore now to code part.
First we will start we the Tray click command which is pretty simple note that is hardcode the id for our application and the database I getting the item from.

 

Now we have something that handle the click on the Tray command no we will code the popup or application window. First the frontend code for the application. The frontend code shows when the user session started and when the last request was made info need to take into account which user sessions you should end

<div class="taskInfoWindow">
  <div class="SystemTime>
  <span class="Literal">System Time</span>
  <span class="Time"><%# SystemTime %></span>
  </div>
    <div class="tasks">
      <asp:Repeater ID="taskRepeater" runat="server" DataSource="<%# ScheduleItems %>"
        OnItemCommand="taskRepeater_ItemCommand">
        <HeaderTemplate>
          <table>
            <thead>
              <tr class="informationHeader">
                <td class="Icon">
                  *
                </td>
                <td class="Literal">
                  Task name
                </td>
                <td class="Literal">
                  Is Due
                </td>
                <td class="Time">
                  Last run
                </td>
                <td class="Time">
                  Next run
                </td>
                <td class="Button">
                  Execute Task
                </td>
              </tr>
            </thead>
            <tbody>
        </HeaderTemplate>
        <ItemTemplate>
          <tr class="scheduledItem <%# Container.ItemIndex == 0 || Container.ItemIndex % 2 == 0 ? "even" : "odd"   %>">
            <td class="Icon">
              <img src="/sitecore/shell/Themes/Standard/<%#((ScheduleItem)Container.DataItem).Icon%>"
                alt="<%# ((ScheduleItem)Container.DataItem).DisplayName %>" />
            </td>
            <td class="Literal">
              <%# ((ScheduleItem)Container.DataItem).DisplayName%>
            </td>
            <td class="Literal">
              <%# ((ScheduleItem)Container.DataItem).IsDue%>
            </td>
            <td class="Time">
              <%# ((ScheduleItem)Container.DataItem).LastRun.ToString("HH:mm dd/MM-yyyy")%>
            </td>
            <td class="Time">
              <%# ((ScheduleItem)Container.DataItem).NextRun.ToString("HH:mm dd/MM-yyyy")%>
            </td>
            <td class="Button">
              <asp:LinkButton ID="LinkButton1" Text="Execute" CssClass="SortButton" runat="server"
                CommandName="Execute" CommandArgument="<%# ((ScheduleItem)Container.DataItem).ID %>" />
            </td>
          </tr>
          </tbody>
        </ItemTemplate>
        <FooterTemplate>
          </tbody> </table>
        </FooterTemplate>
      </asp:Repeater>
    </div>
  </div>

Now to the code behind, I am only using standard sitecore functionality nothing fancy here other then the click event for the repeater that end the selected user session.

using Sitecore.Data;
using Sitecore.Security.Accounts;
using Sitecore.Web.Authentication;

namespace Userhelper.Presentation
{
  public partial class UserSessionsView : System.Web.UI.Page
  {
    protected void Page_Load(object sender, EventArgs e)
    {
      DataBind();
    }

    public IEnumerable UserSessions
    {
      get
      {
        return DomainAccessGuard.Sessions;
      }
    }


    protected void userRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
    {
      string id = e.CommandArgument.ToString();
      switch (e.CommandName)
      {
        case "Execute":
          EndSession(id);
          break;
      }
    }

    private void EndSession(string sessionID)
    {
      DomainAccessGuard.Kick(sessionID);
    }

  }
}


This is how I looks when we done

userhelperlast

The styling part is left for you. With this little userhelper you can set security on the tray icon so local administrator can see the tray icon and all other users have denied read access so only users with elevate security settings can end user sessions.

 

System Alert in sitecore

27. april 2010 by Thomas Stern

When working with large Sitecore implementation and customers that have a lot of editors maintaining the content of their website, often makes updating the system becomes a hurdle. Because editors may or may not work in on the same floor or even the same building, so even if you have an contact person who's responsibility is to notify editor about the downtime, it is possible that one or more editors haven't heart about the downtime and haven't save the what they where doing the second the system is taken offline, a valuable work may have been lost.
So I thought what would be more smart the implementing a System alert/ notifier system that alert the users about the forthcoming downtime. This Alert could as in this example be a simple javascript alert.
My solution is build around simple Javascript calling a webservice which looks for alert placed inside sitecore and display them as a simple javascript alert. Yes some may argue that I have some hardcoded path string and what have we not, but it is left to you to move these to fx. The web.config. Even more this solution I maybe a little over the edge when looking at the implementation, but I se so many usages for this so I went ALL-IN as implemented with interface and using a provider model. The solution is build and tested against a Sitecore 6.2, but nothing wrong with using on other Sitecore versions.
But here goes first the javascript since Sitecore editors have three different editors Page Edit, Content Editor and the Desktop. So we need to include the javascript in three different files, and because of the we need to ensure that the file is only loaded once so logged into the Desktop and opening the content editor doesn't give two warnings, hence the cookie check. Now to javascript, it's all simple stuff.
The javascript should be include in these three files
Webedit:
sitecore\shell\Applications\WebEdit\WebEditRibbon.aspx
Content Editor:
sitecore\shell\Applications\Content Manager\Default.aspx
Desktop:
sitecore\shell\Applications\Startbar\Startbar.xml

/* Function for calling the webservice                             */
function callWebservice() {
  var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  xmlhttp.open("GET", "/Components/SystemNotifier/AjaxWebservice/SystemNotifier.asmx/GetAlerts", false);
  xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  xmlhttp.send();

  if (xmlhttp.status == 200) {
    var str = xmlhttp.responseXml.getElementsByTagName("string").item(0).text;
    return str;
  }
}

/* function that Get system alerts */
/* update timer by calling the webservice    */
function GetSystemAlerts() {
  var alertString = callWebservice();
  if (alertString != "") {
     alert(alertString);
     //increase time to next call so we dont get same alert twice
     setTimeout("GetSystemAlerts()", 125000);
  }
  else {
    setTimeout("GetSystemAlerts()", 60000);
  } 
}

var cookieName = "SitecoreSystemNotifier";

function writeCookie() {
 document.cookie = cookieName;
}

function cookieExists()
{

 if (document.cookie.length >0 )
 {
   var offset = document.cookie.indexOf(cookieName);
   if (offset != -1)
     return true;
   return false;
 }
 return false;
}


function init(){
 if(!cookieExists()){
  writeCookie();
  //SetTimeout in ms
  setTimeout("GetSystemAlerts()", 60000);
 }
}

init();

 

Okay now that we have the javascript we need the webservice to be called. It's fairly simple when using the Provider.

 

namespace SystemNotifier.AjaxWebservice
{
  /// 
  /// Summary description for SystemNotifier
  /// 
  [WebService(Namespace = "http://pentia.dk/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
  [System.Web.Script.Services.ScriptService]
  public class SystemNotifier : WebService
  {

    [WebMethod]
    public string GetAlerts()
    {
      ISystemAlert alert = AlertProvider.NextAlert;
      if (alert != null)
        return alert.Text;
      return "";
    }

    private SystemAlertProvider _provider;
    private SystemAlertProvider AlertProvider
    {
      get
      {
        if (_provider == null)
          _provider = new SystemAlertProvider();
        return _provider;
      }
    }
    
  }
}

And now to the provider implementation

 public class SystemAlertProvider
  {
    private IEnumerable _alerts;
    public IEnumerable Alerts
    {
      get {
      if(_alerts == null)
       _alerts = GetAlertsFromSitecore();
       return _alerts;
      }
    }

    private TimeSpan timespan = new TimeSpan(0, 1, 0);
    private IEnumerable GetAlertsFromSitecore()
    {
      ChildList childList = AlertRootItem.Children;
      foreach(Item child in childList)
      {
        ISystemAlert alertItem = new SystemAlert(child);
        if(alertItem.AlertTime > DateTime.Now.Subtract(timespan))
          yield return alertItem;
      }
    }

    private const string sitecoreRootPath = "/sitecore/system/SystemAlertNotifier";
    private Item _rootItem;
    private Item AlertRootItem
    {
      get
      {
        if(_rootItem == null)
         _rootItem = Database.GetItem(sitecoreRootPath);
        return _rootItem;
      }
    }

    private const string _databaseName = "master";
    private Database Database
    {
      get
      {
        return Database.GetDatabase(_databaseName);
      }
    }
    public ISystemAlert NextAlert
    {
      get
      {
        if(Alerts.Count() > 0)
          return Alerts.OrderBy(w => w.AlertTime).First();
        return null;
      }
    }
  }

And finally the Alert interface and implementation of the same.

Inteface

public interface ISystemAlert
  {
    DateTime AlertTime { get; }
    String Text { get; }
  }

Implementaion

public class SystemAlert : ISystemAlert
  {
    public SystemAlert(Item item)
    {
      Item = item;
    }

    private Item Item
    {
      get;
      set;
    }

    private const string _alertTimeField = "SystemAlert_AlertTime";
    public DateTime AlertTime
    {
      get
      {

        DateField dateField = Item.Fields[_alertTimeField];
        return dateField.DateTime;
      }
    }

    private const string _textField = "SystemAlert_Text";
    public string Text
    {
      get { return Item[_textField]; }
    }
  }

Now we got all the code working so now we need to have someway to get the info, let's use a sitecore item. So here is a snapshot of the how my sitecore item looks.

sys2

So this is pretty much everything you need to have a system alert system up and running inside sitecore. Remember to edit hardcode root path to system alert root folder.
You can download the project in the download section link here.

And hope you can see the posiblities in this solution or implementaion, you could scheduled downtown and have email alert, downtime calendar and much much more hope you enjoy,