Archive

Archive for December, 2009

Custom Tokens and NVelocity for Item Creation

December 29, 2009 2 comments

We’d all be familiar with the tokens you can use in template standard values which get replaced on item creation in Sitecore. They are a great way to set some default field values based on some other data such as the name given to the item or the current date.

The following lists the tokens available in Sitecore 6.2.

$name
The name of the item being created
$id
The ID of the item being created
$parentid
The ID of the parent of the item being created
$parentname
The name of the parent of the item being created
$date
Date part of the current datetime in Sitecore ISO format
$time
Time part of the current datetime in Sitecore ISO format
$now
The current date in Sitecore ISO format

I remember being told during the Sitecore 5 time frame that token replacement used NVelocity. In fact, I’m sure I remember it being in the training material. Well in Sitecore 6 it doesn’t appear as if it is. The tokens look like Velocity variables, but that’s just coincidence.

But could I get some value from using NVelocity to process the replacements?

Firstly, let’s just look at adding our own token into the replacement. It’s actually quite simple. All we have to do is add a processor to the expandInitialFieldValue pipeline. So let’s go ahead and create a processor which will insert the domain for the current request (which is quite commonly the same URL used to access the public site).

using Sitecore.Pipelines.ExpandInitialFieldValue;
using System.Web;

namespace CustomReplacers
{
  public class DomainReplacer : ExpandInitialFieldValueProcessor
  {
    public override void Process(ExpandInitialFieldValueArgs args)
    {
      var context = HttpContext.Current;
      if(context != null)
      {
        if (args.SourceField.Value.Contains("$domain"))
        {
          var uri = context.Request.Url;
          args.Result = args.Result.Replace("$domain", uri.Scheme +
            "://" + uri.Host);
        }
      }
    }
  }
}

And add our custom processor to the expandInitialFieldValue pipeline.

<processor type="CustomReplacers.DomainReplacer, CustomReplacers" />

Add this processor at the bottom of the pipeline below the default Sitecore processors to allow Sitecore to do it’s thing before our custom processor gets involved. As with other pipeline processors you can abort a run in a processor and the expandInitialFieldValue pipeline is no exception; note the SkipStandardValueItems processor. You don’t want your processor doing extra work it didn’t have to.

Inside the custom processor you’ll notice all the magic happens around the ExpandInitialFieldValueArgs parameter which is passed into the Process method. You have access to the source field through the SourceField property where you can grab the current value to process. We need to put our output into the Result property. But seeing as though our processor is not the first to run, we’ll want to process the existing Result property, not the original field value as the previous processors may have already altered the result and if we just processed the source field value again we’d lose the processed value.

After you’ve adding this processor in and setup a template’s standard values with the $domain token when you now create an item based on this template the token will be replaced with the current domain URL.

As you can see, adding your own tokens is pretty simple. But let’s explorer the idea of using NVelocity to process the fields. We’ll of course need to start with a new custom processor. This processor will use NVelocity and add the item being currently processed into the context for use.

using System.IO;
using NVelocity;
using Sitecore.Pipelines.ExpandInitialFieldValue;

namespace CustomReplacers
{
  public class VelocityProcessor : ExpandInitialFieldValueProcessor
  {
    public override void Process(ExpandInitialFieldValueArgs args)
    {
      if (args.SourceField.Value.Contains("$item"))
      {
        var context = new VelocityContext();
        context.Put("item", args.TargetItem);
        NVelocity.App.Velocity.Init();
        var output = new StringWriter();
        NVelocity.App.Velocity.Evaluate(context, output,
          "VelocityProcessor", args.Result);
        args.Result = output.GetStringBuilder().ToString();
      }
    }
  }
}

We’ll of course also need to add the processor to the pipeline.

<processor type="CustomReplacers.VelocityProcessor, CustomReplacers" />

Objects you put into a velocity context are not just strings; they are real objects which you can interact with and call on their properties and methods.

Now we can do some pretty cool stuff using VTL (Velocity Template Language). Just add some of these into your template standard values.

The name of the item:
$item.Name
The path of the item:
$item.Paths.FullPath
The name of the item’s grandparent:
$item.Parent.Parent.Name
The ID of an ancestor whose key is “home”:
$item.Axes.SelectSingleItem("ancestor::*[@@key='home']").ID

That’s right, we can use Sitecore Query in our item creation now. This is because the Item class is a very rich class which gives access to a lot of functionality. In the above Sitecore Query example I use the Axes property of the class so I can execute a Sitecore Query using the SelectSingleItem method. I could also use the SelectItems method to return an array of items. And VTL allows me to iterate over those items.

<ul>
#foreach($i in $item.Axes.SelectItems("
  ancestor::*[@@templatekey='sample item']"))
  <li>$i.Name</li>
#end
</ul>

To learn more about VTL check out the Velocity User Guide. Note that VTL used by NVelocity should follow that used by Velocity (the original Java project which NVelocity is based on).

And I should also point out an article which I stumbled upon whilst writing this article. Looks like our man Alexey Rusakov beat me to this idea way back in 2005. Check out his Velocity master variables replacer.

Categories: Sitecore

Sitecore 6.2 RSS

December 9, 2009 14 comments

When Sitecore 6.2 was released I wrote a post about some of the new features included in the release. One of which was inbuilt RSS. I think this feature was quite underplayed on my part, so allow me to make a song and dance about it here.

The RSS capabilities of Sitecore 6.2 comes in 2 parts. The first is standard content RSS. It’s now quite easy for content authors to create RSS feeds for their content. The Shared Source RSS module always allowed us to do that but now it’s handled natively. The RSS module is also quite old and had to provide it’s own way of doing certain things that have since been added or changed in the CMS.

Content RSS feeds in Sitecore 6.2 are very easy to create and have been designed to allow non-developers to create and maintain them. There are 2 parts to creating a content feed. Firstly you have to define the appearance of the feed; which data fills in which RSS element. This is done using the feed designer form which sits on the presentation tab in the content editor. The feed design is saved back to the item’s template standard values so all items based on the same template will look the same. After all, you don’t want to have to go through every single item and define it’s feed presentation.

feed designer

The feed designer presents a selection of fields you can use to populate each of the RSS elements. And this would be fine for most content authors. But what if you need to create a more complex aggregation? What if you want to generate the body of the feed based on several fields, or even refer to other items as well? All the feed designer is doing is setting a presentation for the RSS device of the item’s template standard values. This is the same way in which you’d define presentation for any other kind of device. You’ll notice after you’ve designed the feed that the feed device now uses the “Feed Delivery Layout” layout and the FeedRenderer control. Being that this is standard presentation definition you could change this if you needed more flexibility in designing the feed.

After we’ve designed the item’s feed presentation we need to define a feed item which when requested will output the RSS. This is as simple as creating an item in the content tree which inherits from the /sitecore/templates/system/feeds/rss feed template. You’ll need to fill in the items field which points to the parent of the items you want to syndicate through the feed.

If we need more control over the behavior of how the feed works we can also specify a custom type in the type field of the feed item to override these default behaviors. This custom feed type must inherit from Sitecore.Syndication.PublicFeed.

Let’s say I wanted to be able to filter based on template, so only items that are based on certain templates will be included in the feed. I would first create a new template which inherits from the rss feed template and add an additional field Include Templates of type TreeList to my new template. I could then use the following class in the type field of a feed created from that template to perform the filtering.

using System.Collections.Generic;
using System.Linq;

namespace Sitecore_Custom_Feed
{
  public class CustomFeed : Sitecore.Syndication.PublicFeed
  {
    public override IEnumerable<Sitecore.Data.Items.Item>
      GetSourceItems()
    {
      string templateIDs = FeedItem["Include Templates"];
      var items = base.GetSourceItems();
      return items.Where(i => templateIDs.Contains(
        i.TemplateID.ToString()));
    }
  }
}

Now only items which are based on any of the templates in the Include Templates field will be included in the feed.

Another handy extension to have on each page is the ability to check if an item contains a feed item as a child and provide the alternate link tags in the html head to have the browser detect the feed for this page. Firefox for example will display an RSS icon on the right-hand side of the address bar if the page contains an alternate link.

rss on page

Some XSLT you might use to output that tag:

<xsl:for-each select="child::item[@template='rss feed']">
  <link rel="alternate" type="application/rss+xml"
    href="{sc:path(.)}" title="RSS feed"/>
</xsl:for-each>

Just make sure your alt link rendering is included in the head section of your HTML.

Content authors can also simply link to the feed item after they’ve created it in the rich text editor.

After publishing your changes you’ll be able to see the feed on the live website.

content rss feed

The second part of the new RSS capabilities in Sitecore 6.2 is Sitecore Client Feeds. Sitecore now allows you to subscribe via RSS to a workflow, workflow state or individual content item’s updates. This is really awesome cause it means content approvers no longer have to log into Sitecore each day to check their workbox. All the information they would have received from this application is available through RSS, including the ability to execute commands directly from the RSS.

Lars Nielsen noted in a comment on my original Sitecore 6.2 new features post that he has subscribed to workflow using Outlook. I’ve also heard other people calling this client feeds capability “Outlook integration for workflow”. This is a misnomer. Calling this feature as such is actually limiting it’s potential. RSS is more pervasive than just Outlook and Windows. With this capability I can now have native support for workflow information using any RSS program on any platform (or device :) ). I can approve articles directly from my Linux machine (Sitecore supports the content editor cross browser, so I could have already done that, but now I’m using a native application), or from my mobile phone. Anything that can support RSS.

I’m actually quite disappointed in the workflow RSS capability. Why you might ask? Does it lack a particular feature? Does it lack some polish? Do I not like how it works? No. None of the above. Earlier this year I achieved a very similar result using email. Actually I should say it was one of my colleagues as it was he who wrote the thing (thanks Michael!). The basic idea of the Next Digital extended workflow action was to provide an email action that can be included in workflow and provide both extended capabilities in terms of field data and provide workflow commands inside the email. This action was based on Alexey Rusakov’s extended email action. So I’m quite disappointed in the workflow RSS capability because our really cool Sitecore extension is not as unique as it once was.

To subscribe to a workflow feed simply log into Sitecore and open the workbox then click on any of the RSS icons in the UI. The RSS icon in the workflow title bar is of course for the entire workflow and the RSS icon in the title of each workflow state is of course just for that state.

Now whenever an item enters the state (for state RSS) or when it changes state (for workflow RSS) you’ll get a notification in your RSS client. You’ll get a bunch of information about the item along with the available commands you can execute against the item, taking into account your security. This means that if you don’t have the required permissions to the command, you will not see the command in the feed.

And just to prove it really does work outside of Outlook, here’s a few other programs and platforms.

Sitecore workflow RSS on an Android phone – List view.

sitecore workflow rss android

Sitecore workflow RSS on an Android phone – Article view.

sitecore workflow rss android article

Can you imagine as a content approver being able to approve content whilst on the run straight from your mobile phone?

Sitecore workflow RSS in Akregator in Kubuntu (Linux).

Sitecore workflow RSS in Akregator

For more information on RSS feeds in Sitecore 6.2 and for full instructions on how to create them, refer to the Content Author’s Reference and Cookbook for Sitecore 6.2 which can be downloaded from the SDN.

Categories: Sitecore

Firefox Content Editor Scrollbar Issue

December 2, 2009 5 comments

It was a real shame when Sitecore removed Firefox support when they released Sitecore 5.3. In prior versions of Sitecore you still had the option to log into the desktop from the login screen when using Firefox. And even though it didn’t look quite as perfect as it did in IE, it was so much faster thanks to the faster javascript engine in Firefox.

But there are ways around that. Check my previous blog post on how to login to the desktop using Firefox in Sitecore 6.x .

Even though desktop support was removed you could still log into the page and content editors and edit content that way. A little while ago I had a client reporting that the scrollbars were missing in the content editor tree when they were using Firefox. This seemed quite odd, but sure enough, when I checked a Sitecore 6.0.2 site using the latest Firefox (version 3.5.5) after expanding out the content tree and clicking on an item the scrollbars disappeared. Thinking this may have been fixed in a later update I checked a 6.2 site, and the same error could be seen.

ff missing scrollbars

After much investigation and firebugging I finally found the issue. It lies in the /sitecore/shell/Controls/Gecko.js file. On line 495 is the following line:

element.setStyle({ height: "100%" });

This is the culprit. To solve the issue simply comment that line out.

ff scrollbar back

What I believe to be happening; the content tree sits inside a table cell. When you set a style with 100% height that means the element should be 100% of the height of it’s containing element. In this case, a table row. This row also holds the fields section to the right of the content tree, which is much longer than the content tree and will push the height of the table row out. So when we set 100% height on the content tree panel, it should now be the same height as the fields section. At least that’s what I think is happening.

I must point out that I haven’t tested the above solution exhaustivly and this update may affect other areas of the system. I’ve also only checked the solution with the Firefox version mentioned above (3.5.5) and I suspect that this would break earlier versions on Firefox. If that were the case it wouldn’t be too difficult to update the script so instead of commenting out line 495 just detect the browser version and use that in an “if” statement.

I’ve already logged the issue with Sitecore support so this post is just for the interim if you really need a fix and can’t wait for another release from Sitecore. I’m also posting this to explain how I can do some of the things I do in my next post.

Categories: Sitecore
Follow

Get every new post delivered to your Inbox.