We just had a requirement come up on my current project where they wanted to restrict the number of sublayouts of a certain type on a specific page.  They are using the unified page editor, so the thought was that when the "Select a Rendering" dialog popped up, a certain sublayout would only appear if there weren't already X of them on the page.

To me, this had rules engine written all over it.  Placeholder Settings are already used to control which renderings/sublayouts are allowed in a specific placeholder, so that seemed like the natural place to add a Rules field.  I did this using a base template which I named Placeholder Base.  Note - I did this via a base template instead of modifying Sitecore's Placeholder template directly to minimize the impact of future upgrades (at most, I may have to re add my custom base template to their template someday).  Anyway, the template looks like this:

Placeholder Base Template
Figure 1 - Placeholder Base Template

I added this template to the base templates for /sitecore/templates/System/Layout/Placeholder, so now this Rules field shows up on any placeholder settings item.

If you're not familiar with the Rules field type, the source I specified above is a new folder that I created to hold my custom actions and conditions.  Sitecore will add any actions or conditions it finds there to the list of available conditions and actions when you are editing this field.  Here's a screenshot of that folder structure:

Placeholder Settings Rules Folder
Figure 2 - Placeholder Settings Rules Folder

Sitecore has a pipeline specifically for determining which renderings are allowed in a placeholder.  You can find the list of processors in web.config in the <getPlaceholderRenderings> element.  The first processor here is GetAllowedRenderings which is exactly what I want to change the behavior of.  I created my own class that inherits from Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings and overrides the GetRenderings() method:

public class GetPlaceholderRenderingsProcessor : Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings
{
    private ID DeviceId;
    private string PlaceholderKey;
    private Database ContentDatabase;
    private string LayoutDefinition;

    protected override List<Item> GetRenderings(Item placeholderItem, out bool allowedControlsSpecified)

    {
        // ... see attachment for code ...
    }

    public void Process(GetPlaceholderRenderingsArgs args)
    {
        DeviceId = args.DeviceId;
        PlaceholderKey = args.PlaceholderKey;
        ContentDatabase = args.ContentDatabase;
        LayoutDefinition = args.LayoutDefinition;

        // ... code pasted in from Reflector goes here ...
    }
}

Additionally, I later found that I needed to know some additional information in order to evaluate my custom conditions.  Information that is only available within the Process() method of the pipeline processor.  Since Sitecore's Process method is not virtual, I had to create my own, copy/paste their implementation in from Reflector, and add some code to stash the information I needed in some private fields.  You can also see that code above.

In order to pass information in and out of the rules engine, I needed to implement a custom rule context.  Here's what that class looks like:

public class PlaceholderSettingsRuleContext : RuleContext
{
    public List<Item> AllowedRenderingItems { get; set; }
    public ID DeviceId { get; set; }
    public string PlaceholderKey { get; set; }
    public Database ContentDatabase { get; set; }
    public string LayoutDefinition { get; set; }
}

This has properties for the DeviceId, PlaceholderKey, ContentDatabase, and LayoutDefinition that I got from the processor's Process method as well as one for the list of allowed renderings coming out of the rules engine.

So, now I think I can discuss the GetRenderings method on the pipeline processor.  First, I needed to get the initial list of allowed renderings from the base class:

var list = base.GetRenderings(placeholderItem, out allowedControlsSpecified);

Next, I needed to get the rules from the current placeholder settings item that's passed in to the method.  Note - Sitecore stores the rules as an XML string.  If the string is null or empty, I just returned the list from the base class.

string rulesXml = placeholderItem["Allowed Controls Rules"];
if (string.IsNullOrWhiteSpace(rulesXml)) return list;

This is where I constructed the custom rule context that will be passed into the rules engine.  Note, I passed in the initial list of allowed renderings so that the rules may add or remove items from the list:

PlaceholderSettingsRuleContext context = new PlaceholderSettingsRuleContext();
context.Item = placeholderItem;
context.AllowedRenderingItems = list;
context.DeviceId = DeviceId;
context.PlaceholderKey = PlaceholderKey;
context.ContentDatabase = ContentDatabase;
context.LayoutDefinition = LayoutDefinition;

So that just leaves parsing and executing the rules:

var parsedRules = RuleFactory.ParseRules<PlaceholderSettingsRuleContext>(placeholderItem.Database, rulesXml);
RuleList<PlaceholderSettingsRuleContext> rules = new RuleList<PlaceholderSettingsRuleContext>()
rules.Name = placeholderItem.Paths.Path;
rules.AddRange(parsedRules.Rules);
rules.Run(context);

Sitecore wants to know if any allowed controls are being returned or not.  I think this is new in 6.6…  Anyhow, if this is false - Sitecore will allow the user to choose a rendering from a tree view.

if (context.AllowedRenderingItems.Count < 1)
    allowedControlsSpecified = false;
else
    allowedControlsSpecified = true;

And lastly, I returned the final list of allowed renderings:

return context.AllowedRenderingItems;

To activate the pipeline processor, I created a .config file in the App_Config\Include directory that replaces the stock Sitecore GetAllowedRenderings processor with my custom one.  It looks like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:x="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getPlaceholderRenderings>
        <processor patch:instead="*[@type='Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings, Sitecore.Kernel']"
                   type="MyNamespace.GetPlaceholderRenderingsProcessor, MyAssembly" />
      </getPlaceholderRenderings>
    </pipelines>
  </sitecore>
</configuration>

At this point, Sitecore was processing rules when a user wanted to insert a new sublayout through the unified page editor.  But, the rules had no way to evaluate if a sublayout should be allowed or not and had no way to allow or disallow sublayouts.  Enter a custom condition and custom actions.

For my condition, I decided to inherit from Sitecore's IntegerComparisonCondition.  This allowed me to control sublayout visibility based on the existing number of sublayouts of that type on the page using "is greater than" or "is less than".  Here's what the condition code looks like:

public class SublayoutCountCondition<T> : IntegerComparisonCondition<T> where T : PlaceholderSettingsRuleContext
{
    public string SublayoutId { get; set; }

    protected override bool Execute(T ruleContext)
    {
        // Parse the layout definition.
        LayoutDefinition layoutDef = LayoutDefinition.Parse(ruleContext.LayoutDefinition);
        if (layoutDef == null) return false;

        // Convert sublayout id string to ID.
        ID sublayoutId = new ID(SublayoutId);

        // Find the device definition for the current device.
        DeviceDefinition deviceDef = null;
        foreach (DeviceDefinition dd in layoutDef.Devices)
        {
            if (new ID(dd.ID) == ruleContext.DeviceId)
            {
                deviceDef = dd;
                break;
            }
        }
        if (deviceDef == null) return false;

        // Loop through the rendering definitions for the device and count the instances of SublayoutId.
        int renderingCount = 0;
        foreach (RenderingDefinition rendering in deviceDef.Renderings)
        {
            if (new ID(rendering.ItemID) == sublayoutId)
                renderingCount++;
        }
        // Evaluate the condition.
        return base.Compare(renderingCount);
    }

The SublayoutId property gets set by a variable in the condition definition in Sitecore (see Figure 3 below).  The Execute method grabs the layout definition from the context, parses it into a LayoutDefinition object, locates the definition for the device being edited, and counts up the instances of the RenderingId.  Lastly, it calls the base Compare method to do the comparison defined in the rule.  Here's what the condition definition looks like in Sitecore (using the /sitecore/templates/System/Rules/Condition template):

Count Sublayouts Condition
Figure 3: Count Sublayouts Condition Definition

If you're not familiar with custom rules engine conditions, the Type is the class up above and the Text is the text that appears when the condition is added to a rule.  Variables are defined in square brackets using four comma-separated values.  The first variable is SublayoutId from my above class, and the other two variables exist in the IntegerComparisonCondition base class.  Here's a breakdown of the four comma-separated values in the SublayoutId variable:

  • SublayoutId - The name of the property on the condition class to be set.
  • tree - The type of UI element to present to the user for choosing a value.
  • /sitecore/Layout/Sublayouts - The root of the tree used to choose a value.
  • sublayout - The text that's displayed in the rules editor before a value has been chosen.
Here's what the condition looks like when it is first added to a rule (before values have been selected for any of the variables):

Condition Initial State
Figure 4: Condition Initial State

The second half of the rule was the custom actions that allow or disallow a sublayout.  All these needed to do was add (or remove) a sublayout from the context's AllowedRenderingItems property.  Here's the code:

public class AllowSublayoutAction<T> : PlaceholderSettingsAction<T> where T : PlaceholderSettingsRuleContext
{
    public string SublayoutId { get; set; }

    public override void Apply(T ruleContext)
    {
        // Create a new list if one hasn't been created yet.
        if (ruleContext.AllowedRenderingItems == null)
            ruleContext.AllowedRenderingItems = new List<Item>();

        // If the sublayout is already in the context, do nothing.
        if (ruleContext.AllowedRenderingItems.Any(i => i.ID == new ID(SublayoutId)))
            return;

        // Add the sublayout to the context.
        ruleContext.AllowedRenderingItems.Add(ruleContext.Item.Database.GetItem(new ID(SublayoutId)));
    }
}

public class DontAllowSublayoutAction<T> : PlaceholderSettingsAction<T> where T : PlaceholderSettingsRuleContext
{
    public string SublayoutId { get; set; }

    public override void Apply(T ruleContext)
    {
        // If there are no renderings, do nothing.
        if (ruleContext.AllowedRenderingItems == null) return;

        // If the sublayout already isn't in the context, do nothing.
        Item item = ruleContext.AllowedRenderingItems.FirstOrDefault(i => i.ID == new ID(SublayoutId));
        if (item == null) return;

        // Remove the sublayout from the context.
        ruleContext.AllowedRenderingItems.Remove(item);
    }
}

Similar to the condition, the SublayoutId property in the actions gets set by a variable.  Here's what the action definitions look like in Sitecore (using the /sitecore/templates/System/Rules/Action template):

Allow Sublayout Action
Figure 5: Allow Sublayout Action Definition

Don't Allow Sublayout Action
Figure 6: Don't Allow Sublayout Action Definition

As shown in Figure 2 above, the actions and conditions were created in the folder that is referenced in the source for the Rules field in Figure 2.  This made them available when editing that Rules field in Sitecore.  Here's a screenshot of me editing the rule for a placeholder setting:

Editing a Rule
Figure 7: Editing a Placeholder Settings Rule

I've labelled 1) Where the custom condition appears.  2) Where the custom actions appear.  3) An example rule.  Note in the example, I have included three sublayouts (Accessories Layer, Feature Layer, and Gallery Layer) using the usual "Allowed Controls" field on the placeholder settings item.  The rule only disallows the Gallery Layer sublayout if there is already one on the page (the number is greater than 0).

That's about it.  I will edit this post with a link to a demo video and a ZIP file with the source code when I have them prepared.  In the meantime, if you have any questions about this - feel free to reach out to me via Twitter @williamsk000.

I got bit by a small gotchya while setting up a fresh Sitecore 7 install to use SOLR today. In SOLR, I left the core named the default "collection1" instead of renaming it to "itembuckets" as is recommended in the documentation. Well, if you want your core named something other than "itembuckets", you'll need to edit the index definitions in Sitecore.ContentSearch.Solr.Indexes.config to change the core names to reflect your core names.

The error I received was:

Connection error to search provider [Solr] : Unable to connect to [http://localhost:8983/solr]
I kind of wish the error could tell me it couldn't connect to the core I had specified in the .config file, since SOLR itself was running fine and going to the URL shown in the error message worked fine. But if you see this error in the future, you might check that your SOLR cores are correct in your .config files, anyway.

Thanks to Stephen Pope at Sitecore for making several suggestions on Twitter - one of which led me to discover this!

Next on my TODO list - get the Autohaus demo running with SOLR. Will post again once I know if it's possible and how to do it!

Filed under: , ,

Over the weekend, I posted part 1 of a discussion of this thing I created that I’m calling “conditional pages”.  I promised to elaborate on the technical details, so here I am with some code samples and instructions that hopefully will allow others to reproduce what I have done.

Before I begin, let me once again thank Sitecore CTO John West - without whose blog posts and assistance on the SDN forums, I might have never figured this stuff out.  I haven’t found a better source of information on rules engine customizations.  If you’re looking for more information, check out his blog here http://goo.gl/5V4Gg

This whole process begins when a request comes in to the Sitecore pipelines.  By implementing a custom StartTrackingProcessor, I am able to intercept the request, inspect the item that was requested and if it is a conditional page, execute the rules defined on the page to determine what item should replace the originally requested item in the Sitecore context.

To create my custom processor, I just created a class that derives from Sitecore’s StartTrackingProcessor and implemented an override for the Process() method.  The declaration looks like this:

    public class ConditionalPageTrackingProcessor : 
StartTrackingProcessor { public override void Process(StartTrackingArgs args) { } }

Within my Process() method, I first check if Sitecore.Context.Item is using the Conditional Page template.  I described this template in my previous post, but here’s a quick screenshot of what it looks like in Sitecore:

Screen Shot 2012-06-11 at 9.45.58 PM

I’ll explain the path you see in the source for the Rules field in a little bit.

If the requested item is not a conditional page, then there is nothing for my processor to do so the code just returns.  However, if it IS a conditional page – I pull the value from the Rules field like this:

    string rulesXml = Context.Item["Rules"];

Note, there is no strongly-typed Field-derived class for fields of type Rules, so I’m pulling out the raw XML string.  This string now needs to be parsed, and that is done by a method on the Sitecore.Rules.RuleFactory class - ParseRules<T>():

    var parsed = RuleFactory.ParseRules<ConditionalPageRuleContext>
(Sitecore.Context.Database, rulesXml);

Now you may be asking – what is this ConditionalPageRuleContext class?  This is my custom rules engine context.  It derives from Sitecore.Rules.RulesContext and adds a property that indicates what item (if any) the rules want the user redirected to.  The concept of a rules context seems a bit confusing at first, but suffice it to say that this class is what allows the rules engine to communicate information back to the code that executes it.  Here’s what the ConditionalPageRuleContext class looks like in code:

    public class ConditionalPageRuleContext : Sitecore.Rules.RuleContext
    {
        public Item NewItem { get; set; }
    }

In order to proceed, I need to construct an object of this class and tell it what item it is acting on:

    ConditionalPageRuleContext ruleContext = 
new ConditionalPageRuleContext(); ruleContext.Item = Context.Item;

And then, I run the rules that were parsed – passing in my rule context:

    RuleList<ConditionalPageRuleContext> rules = 
        new RuleList<ConditionalPageRuleContext>();
    rules.Name = Context.Item.Paths.Path;
    rules.AddRange(parsed.Rules);
    rules.Run(ruleContext);

When this returns, I check if ruleContext.NewItem was set and if it was, I set Sitecore.Context.Item = ruleContext.NewItem.

So wait – how does the NewItem property on my context get set by the rules engine?  Let’s take a closer look at an example rule that redirects some users to a page “Test 1” and others to “Test 2” instead:

image

The actions here are “set current item to …”.  This is a custom rules engine action that only runs within a ConditionalPageRuleContext and sets the NewItem property of that context to the specified value.  Here’s the code:

    public class SwitchToItemAction<T> : 
        RuleAction<T> where T : 
            Rules.ConditionalPageRuleContext
    {
        public string ItemId { get; set; }

        public override void Apply(T ruleContext)
        {
            ruleContext.NewItem = 
                ruleContext.Item.Database.GetItem(new ID(ItemId));
        }
    }

If you haven’t seen a custom action before, the public property gets set by Sitecore to the value selected in the rule editor dialog (the ID of “Test 1” or “Test 2” in the above example), and the Apply() method gets called only for the actions whose conditions pass.  Making this action available in the rules editor requires something in Sitecore.  Remember that source path on the Rules field for the Conditional Page template?

Inside the /sitecore/System/Settings/Rules/ folder, I created a folder named “Conditional Page”.  Within that folder, I created a folder named “Actions”.  The rules editor looks at the field’s source and restricts the actions that can be chosen to those within the “Actions” sub-folder.  So, to configure my action, I created an item in this Actions folder named “Switch to Item” using the /sitecore/templates/System/Rules/Action template.

There are two fields to fill in on this item – Text and Type.  Type is simply a pointer to the above SwitchToItemAction class (in the usual Namespace.Class, Assembly format).  Text is special.  Mine looks like this:

image

The text outside of the square brackets is what the user will see in the rules editor.  Inside the square brackets are four comma-separated values:

ItemId – The name of the property on the class to be set to the selected value.

Tree – The type of UI element to present to the user for choosing a value.

root=… – An argument passed to the UI element.  In this case, the root item for the tree.

an item – The text to show in the rules editor before an item has been chosen.

Note – I haven’t found a documented list of available UI elements and what their arguments are.  I just saw another example using “Tree”.

There’s one more thing I found it necessary to do to make everything work…  My StartTrackingProcessor was never called if the conditional page item didn’t have a layout set on it.  Instead, Sitecore would abort in an earlier pipeline and display the “layout not found” error.  I just fixed this by setting the standard values for the Conditional Page template to use a blank layout.

If you read Part 1 of this series, I talked quite a bit about a Page Design condition…  Reading and writing data from a site visitor’s profile is pretty simple, but I’ll cover that and how to create a custom rules engine condition in my next blog post. :)

I hope this helps somebody.  Please, let me know if you use this idea or think of any ways I could improve it.  And if you have any questions, just send them to me via direct message on Twitter - @williamsk000.

A few days ago, I posed a question here (and on Twitter and on the SDN forum and some other places) regarding accessing visitor profile data from the HttpRequestBegin pipeline.  I promised to post about why I needed to do that once I had everything working, so here’s the story.  Sorry – it’s a bit of a long one. :)

Introduction

Recently, an NTT Data client asked me if I could do something with Sitecore that I had never imagined before…  Essentially, they wanted to A/B test multiple pages across a visit to their site.  If a visitor saw homepage A, they would also see the “A” versions of landing pages, etc.  The changes between versions were not minor – every rendering on a page was likely to change between the “A” and “B” version, some renderings would use completely different datasource templates, etc.

First, let me say - I completed OMS training and certification a year and a half ago, but this was my first real marketing suite request.  The client’s site is being built on current Sitecore 6.5, meaning the latest DMS features were available - but I had little familiarity with them.  I’ve developed a working solution, but I imagine there may be a better way to accomplish it.  If you know of one, I would love to hear about it!

Anyhow - I could not figure out a way to do this with stock DMS features (based on my limited experience), but eventually I thought of a simple customization that would make it possible…  A custom rules engine condition that would check a visitor’s profile for a “Page Design” key with a value of “A” or “B”.  If they did not yet have a value, randomly choose one, save it to their profile, and then make a comparison.

The Page Design Condition

Screen Shot 2012-06-10 at 1.58.45 AM

With this in place, I was able to set personalization rules on multiple renderings on a page, and have them each evaluate my custom condition (e.g. except where the visitor’s page design is “A”, hide component – see above screenshot).  This worked, but it made a crazy mess for content authors within the page editor…  Both versions of the page were visible at the same time and it was terribly confusing to try and determine which renderings were meant to appear on which page version, much less edit any content.

Back to the drawing board, then.  What I needed was a way to create two separate items in the content tree so they could be edited separately, but allow them to share the same URL or have an alias that redirected to them without changing the URL seen by visitors.  I went down many dead end roads trying to realize this idea, but the concept I ended up with is what I’m calling a “conditional page” – an item with rules applied (when the visitor’s page design is “A”, change the current item to “Homepage A”.  When the visitor’s page design is “B”, change the current item to “Homepage B”) that changes Sitecore’s context item to allow the page that was requested (the conditional page) to appear as the alternate page.

I present for you here the working solution I eventually landed upon.

Conditional Pages

A conditional page defines a set of rules under which the site visitor may be “redirected” to another item which will then appear to the visitor in place of the originally requested page.

Screen Shot 2012-06-10 at 2.04.15 AM

The above screenshot is of an item using the Conditional Page data template.  It is named “demo” and is a child of the site’s homepage.  Here, I am using the page design condition discussed above along with a custom action that causes the Sitecore context item to change (I’ll discuss how momentarily).  The effect is that when a visitor goes to the /demo URL, they will see either Landing Page A or Landing Page B depending on which page design has been chosen for them during their visit.

The Conditional Page data template has just one field – Rules – of type “Rules”.  The Source for this field is set to a folder I created under /sitecore/system/Settings/Rules named “Conditional Page”.  This folder has sub-folders named Actions and Conditions containing my custom actions and condition respectively.  For more information about this folder structure, you can read John West’s blog post about using the rules engine here http://goo.gl/aadSf

The key to making conditional pages work was to intercept the initial item request, execute the rules stored in the Rules field, and change Sitecore.Context.Item early enough in the process that everything else would treat it like that was the page requested all along…  It took some trial and error (see previous question about the HttpRequestBegin pipeline) and a little help from John West on the SDN forum, but I eventually found just the spot – the StartTracking pipeline just before the ProcessItem processor.

The technical details of how all of this is done will fill up another blog post, so I’ll save that for later (I promise to post in the next day or two).  But if you’re really interested now – take a look at John West’s post I linked above where he is doing something similar using the rules engine to change the context device for certain requests.

By the way – conditional pages also work with other DMS conditions, not just my custom “page design” one.  So you can have the page that comes up be based on GeoIP rules or if a visitor has achieved a DMS goal or search engine keywords, etc, etc.  It’s kind of like a rules-powered alias.

I hope this concept will be useful to someone else. :)  If you have any questions, comments, suggestions, etc – please reach out to me on Twitter @williamsk000.  Thanks for reading!

Sorry that this isn't a tutorial-style post today, but I promise if I get all of this working that I will post the full story in a few days. For now, I'm having a Sitecore problem...

I have an HttpRequestBegin pipeline processor that runs right after the stock ItemResolver. I need for it to read a value from the current visitor's profile (or if a value isn't there, write one). Here's what my code looks like:

var currentRow = Sitecore.Analytics.Tracker.CurrentVisit.Profile.FirstOrDefault(row => (row.ProfileName == "foo" ));
if ( currentRow == null )
{
   currentRow = Sitecore.Analytics.Tracker.CurrentVisit.CreateProfile("foo");
   currentRow.PatternValues = "bar";
   currentRow.AcceptChanges();
   Sitecore.Analytics.Tracker.CurrentVisit.AcceptChanges();
}

This code when placed in a sublayout works as expected - the first time, the profile value is created and subsequent times it is read back out. However, doing this in the HttpRequestBegin pipeline seems to have no effect at all. Anyone know - is there no way to interact with the current visitor's profile in the pipeline?

There's actually a lot more going on here involving rules engine, etc... As I said, I'll post the full story if I can get it working 'cause it's pretty neat. But for now, I have to get past this particular hurdle...

If you want to contact me regarding this post, it's probably best to use Twitter. You can DM me at @williamsk000

Filed under: , , ,

A while back, I wrote about opening Sitecore’s media library browser from a button in the Content Editor ribbon.  Recently, I decided it would be really useful to do something similar with the Field Editor dialog – only this time, from the Page Editor’s ribbon.  If you’re not familiar with the dialog in question, here’s a screenshot:

FieldEditor

Adding a button to the page editor ribbon isn’t a big deal – it’s very similar to how I added the Thumbnail button to the content editor ribbon.  Essentially, I just opened the Core database, drilled down to /Applications/WebEdit/Ribbons/WebEdit/Page Editor/Edit and added a new Large Button.  This adds the new button to the “Edit” chunk in the “Home” strip.  Here’s a screenshot of that:

Button

And here’s what it looks like in the page editor:

Ribbon

I used a .config file (placed in /App_Config/Include so that Sitecore will automatically load it) to hook the “example:MetadataButton” command to a class in a DLL.  That looks like this:

   1:  <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
   2:    <sitecore>
   3:      <commands>
   4:        <command name="example:MetadataButton" type="Kevin.MetadataButton, MetadataButton" />
   5:      </commands>
   6:    </sitecore>
   7:  </configuration>

So all that leaves is the code to actually open the field editor…  I had to do some digging with .NET Reflector to figure this out.  Thank you Sitecore, for not obfuscating this stuff!  Here’s the critical bit of code:

   1:  // Get the field descriptor objects.
   2:  ListString fieldNames = new ListString("Title|Meta Description|Meta Keywords");
   3:  var fields = fieldNames.Select(f => new FieldDescriptor(item, f));
   4:   
   5:  // Set the field editor options.
   6:  PageEditFieldEditorOptions options = new PageEditFieldEditorOptions(form, fields)
   7:  {
   8:      Title = "Edit Metadata",
   9:      Icon = string.Empty
  10:  };
  11:   
  12:  // Show the dialog.
  13:  SheerResponse.ShowModalDialog(options.ToUrlString().ToString(), "720", "520", 
  14:      string.Empty, true);
  15:  args.WaitForPostBack();

What this does is creates a list of FieldDescriptor objects for the named fields on the current item, constructs a PageEditFieldEditorOptions object, and calls SheerRespose.ShowModalDialog().  Just like in my ThumbnailButton module code, we wait for a postback.  When that postback comes in, this one line of code takes the values from the field editor and saves them into the current item:

   1:  PageEditFieldEditorOptions.Parse(args.Result).SetPageEditorFieldValues();

I’ll attach the full .cs file and .config file to this post once it’s live on my blog in case you don’t want to mess with all of the details yourself.  UPDATE:  Attachments don't seem to be visible to everyone on the DNM site, so here's a link to the ZIP file:  http://www.killeverything.com/zak/MetadataButton.zip

In other news – as of April of 2012, my employer (The Revere Group) have taken on the parent company’s name.  Along with several other affiliated companies in North America, we are now collectively known as NTT DATA.  I’m proud to be a part of the NTT DATA family here in the Americas, and am looking forward to new opportunities the combined companies will be able to tackle!

Filed under:

Something I have wanted to know how to do for years, but hadn’t bothered to put any thought into…  You know that “Folder” tab that appears in the content editor when you create a new folder (using the /Templates/Common/Folder template)?  Here’s a screenshot:

FolderTab1

I have never known how to get this to appear for my own folder types.  Let me see if I can come up with an example…  Let’s say I had a Gallery template that allows you to create individual Photo and Video items underneath it.  So, I would create my templates, and on the Gallery template’s standard values, I would set the insert options to allow creation of Photo and Video items.

I then create a new Gallery item in the content tree and select it as in the above screenshot, but note – the Content tab is displayed by default!

FolderTab2

I just want the same behavior as Sitecore’s “Folder” template…  But how is that done?  Turns out it is very simple.  In the Appearance chunk of the Configure slice of the ribbon, there’s a button named “Editors” – this is the key.  If you go look at the standard values for Sitecore’s “Folder” template, and click on the Editors button, you’ll see something like this:

FolderTab3

It’s as easy as replicating this setting on my Gallery template’s standard values.  After doing that (and adding a couple of photos and a video as children), here’s what my demo gallery looks like now:

FolderTab4

Man, that was easy.  Don’t know why it took me so long to look into doing it – it will definitely come in useful in the future!

Sitecore comes with some other editors you may find a use for – just experiment.  Also, it’s possible to create your own custom editor tabs and make them available for selection in the Custom Editors dialog.  I haven’t done it yet, but it’s on my list of things to try out someday.  Here’s a link to a blog post Mark van Aalst wrote on the subject:  http://www.markvanaalst.com/sitecore/creating-a-item-editor/

Filed under: ,

When I set out to create an easier way to set thumbnails in Sitecore, I had a picture in my mind of how I wanted it to work, but I didn’t really know where to start!  I hadn’t built a custom ribbon button before.  I hadn’t created a modal dialog before nor did I know how to display an existing one.  And I had no idea how to receive results from one even if I could display it.  I had a lot of digging to do, so I fired up Google and .NET Reflector and got to work.

The first step was figuring out how to put a button where I wanted it in the ribbon with the text and icon I wanted to display.  Google gave me a few blog posts discussing this, but I never found exactly the info I was looking for.  I ended up just playing with it until I got it to work, but in the process this is what I figured out:

  • The ribbon is made of tabs or “strips”.  These appear across the top of Content Editor windows and I’m sure you’re familiar with them…  “Home”, “Navigate”, “Review”, “Analyze”, “Publish”, etc.  There is a template used to create these – /sitecore/templates/System/Ribbon/Strip.
  • These strips are then broken into “chunks”.  These are the boxes that group the buttons in the ribbon.  For example, in Content Editor’s “Home” strip, you would see these chunks:  “Edit”, “Insert”, “Operations”, “Clipboard”, “Rename”, and “Sorting”.  The template used to create chunks is /sitecore/templates/System/Ribbon/Chunk.
  • Lastly, within chunks there are buttons.  And there are different types of buttons – small buttons, large buttons, buttons that make a drop-down appear...  I used a large button (template is /sitecore/templates/System/Ribbon/Large Button) for my thumbnail button, but it looks like there are a lot of interesting possibilities here.
  • If you want to add your own strips, chunks, and/or buttons to the ribbon, open up the Core database and drill down to /Applications/Content Editor/Ribbons.  Note these are built to be re-usable, so there is a folder for strips and a folder for chunks – the Default ribbon has children using the Reference template (/sitecore/templates/System/Reference) pointing to the actual strips (so the same strip can be pointed to multiple times) and each strip’s children also use references pointing to its chunks (so the same chunk can appear in multiple strips).  It doesn’t appear that Sitecore is using references for buttons and I didn’t try it, but it may be possible to place buttons into multiple chunks that way as well.

So to get into specifics, I wanted to add a Thumbnail button to the Appearance chunk on the Configure strip (this is where Sitecore placed their “Icon” button, so it seemed like a good fit).  So in the Core database, I drilled down to /Applications/Content Editor/Ribbons/Chunks/Appearance and added a new Large Button using the /sitecore/templates/System/Ribbon/Large Button template.  Here’s a screenshot:

ThumbnailFields

I think the fields in this screenshot should be self-explanatory except for maybe “Click”.  This is the event you want raised when your button is clicked.  If you’re not familiar with these events, take a look at App_Config\Commands.config in one of your Sitecore solutions – it lists a ton of Sitecore’s internal events and the classes that handle them.

After adding the above, my button was already appearing in the ribbon - it just didn’t do anything when I clicked it.  If you looked at the Commands.config file, the next step may be clear...  I needed to wire my event (“example:Thumbnail” in the screenshot) to a class.  I took a look at some of the classes in Commands.config using .NET Reflector and discovered they all implement the Sitecore.Shell.Framework.Commands.Command abstract base class.

So, I created my own class that inherits Command and implemented the Execute() method.  I then created the following config include file and placed it in App_Config\Include\

 <configuration xmlns:patch= "http://www.sitecore.net/xmlconfig/ " 
                xmlns:x= "http://www.sitecore.net/xmlconfig/ ">
   <sitecore>
     <commands>
       <command name= "example:Thumbnail " type= "Example.Namespace.Class,Example.Assembly " />
     </commands>
   </sitecore>
 </configuration>
 

After doing this, I was able to place a breakpoint in my Execute() method and see that it was indeed being hit when I clicked the button.  Success!  But, what should my code inside of Execute() look like?  This is the point where I had to do a LOT of digging on Google and using Reflector.  I knew I wanted to display Sitecore’s media browser, but the question was HOW?

I won’t bore you with the entire process I went through (I don’t remember half of the things I tried anyway).  This is the key bit of code, though:

 UrlString  url = new  UrlString (UIUtil .GetUri("control:Sitecore.Shell.Applications.Media.MediaBrowser" ));
 Item  folderItem = masterDb.GetItem("/sitecore/media library/Images" );
 url["ro" ] = folderItem.Uri.ToString();
 SheerResponse.ShowModalDialog(url.ToString(), true );

As far as I can tell, "control:Sitecore.Shell.Applications.Media.MediaBrowser” is Sitecore’s “magic string” identifying the media browser dialog.  This code builds a URL pointing to the dialog with a querystring parameter identifying the root item for the browser (in this case, /sitecore/media library/Images), and then shows the dialog.

Initially, I placed this code in my class’ Execute() method.  After re-compiling, I was able to click the button and see the media browser, choose an image, and click OK.  But then nothing happened...  No way to capture what image was selected and save it to the database!

After lots and lots more exploring and experimenting, I found that I needed to start a client pipeline before showing the dialog and then wait for a postback in order to capture its result.  Here’s the minimal code to start the pipeline:

  NameValueCollection  parameters = new  NameValueCollection ();
  Context.ClientPage.Start(this , "Run" , parameters);
 

This basically tells Sitecore to start a pipeline and execute the “Run” method in the current class.  So I moved the ShowModalDialog() code into a new Run() method in my class.  The method gets passed a ClientPipelineArgs object, so the method signature looks something like this:

 protected  void  Run(ClientPipelineArgs  args)
 {
 }
 

Calling WaitForPostBack(true) on the args parameter after showing the dialog causes the pipeline to call my method again when a postback occurs (like, when the media browser dialog is closed).  When that happens, args.IsPostBack will be true AND args.Result will contain the dialog’s result.  So the Run() method skeleton looks like this:

 protected  void  Run(ClientPipelineArgs  args)
 {
     Database  masterDb = Factory .GetDatabase("master" );
 
     if  (args.IsPostBack)
     {
         // Do something with args.Result here.
      }
     else
      {
         // If this is not a postback, we want to display's media browser dialog.
          UrlString  url = new  UrlString (UIUtil .GetUri("control:Sitecore.Shell.Applications.Media.MediaBrowser" ));
 
         // We want the browser to be rooted in the Images folder in the media library.
          Item  folderItem = masterDb.GetItem("/sitecore/media library/Images" );
         url["ro" ] = folderItem.Uri.ToString();
 
         // These two lines displays the dialog and causes a postback to this method when it is closed.
          SheerResponse .ShowModalDialog(url.ToString(), true );
         args.WaitForPostBack(true );
     }
 }
 

One last thing I want to cover.  I wanted to limit when my button would appear (it doesn’t make sense to set a Thumbnail for items other than renderings and templates, I don’t think).  Luckily, this is pretty easy.  In your Command class, you can override the QueryState() method and have it return a CommandState telling Sitecore if your button should be enabled, disabled, or hidden.  It receives a CommandContext object that can be used to find out what the current item is in the Content Editor.

I’ll take this opportunity to once again link my full Thumbnail Button Module ThumbnailButtonModule-1.zip which contains the full source.  In addition to what I’ve covered here, it contains the QueryState() code, the code to update the Thumbnail field on the current item, and some code that passes the existing thumbnail in to the media browser so that it’s pre-selected when the dialog opens.

Being able to display Sitecore’s media browser dialog to allow a user to choose or upload an image will come in handy for other projects, so I’m really glad I put the effort into figuring out how to make it work.  Hopefully, I’ve been able to share this knowledge here and other people will be able to take advantage of it as well!  If you have any questions, comments, critiques, etc – please post a comment here on the blog or reach out to me via Twitter – @williamsk000.

with 2 comment(s)
Filed under:

ThumbnailModule

Once I discovered how helpful it was to present thumbnails to content authors in the Page Editor, I started adding them to all of the templates and renderings that I created.  Very quickly, I started to get frustrated with the process (see my part 1 post for the details)...  I don’t like to leave standard fields visible all of the time, so having to turn those on and off and having to dig through them to find the Thumbnail field got old fast.  So I had an idea – add a custom button to the ribbon that would let me choose a thumbnail quickly without all of that hassle. 

The process of building a custom ribbon button, making it pop open the media browser dialog, handling the result, and updating the item’s thumbnail was rather complex and involved a bit of a learning curve for me.  So I’ve decided to leave the in-depth discussion for another blog post and just give the final solution here.

So, may I present - ThumbnailButtonModule-1.zip

The zip file contains a ready-to-go Sitecore package ZIP that you can install using Sitecore’s package installation wizard as well as the source code if you want to see how it was done.  If you compile the source yourself, you will need to update the namespace, class, and assembly name in the config file before placing it in your site’s App_Config\Include directory.  Also, if you don’t install the package then you will need to add the button to the ribbon in the Core database yourself.

I’m already working on another post for later today detailing how I built this module, so if you’re interested in the gory details - I'll have something for you soon!  And if you have any questions or comments or critiques, please let me know either in comments here or via Twitter @williamsk000.

with no comments
Filed under:

With Sitecore 6.4 and 6.5, the new unified Page Editor is wonderful for giving content authors a friendlier experience and more control over their page layouts.  I was never a fan of the page designer in previous 6.x releases, so I was thrilled to see the Page Editor changes.  One of the coolest parts, in my opinion, is the support for thumbnails when inserting new components or pages.  If you’re not familiar with them, here are a couple of screenshots.

InsertPage InsertRendering

Template and Rendering Select Dialogs with Thumbnails Provided

Now I may not be using it exactly as intended (anyone who knows for sure, feel free to comment), but I think this is a great opportunity to give content authors a more friendly interface for choosing what rendering to stick in a given placeholder or what type of page they want to create.

So how do you do it?  Today, I’ll show you the hard way using built-in Sitecore functionality.  Then tomorrow I’ll introduce a module I came up with to make the process much more streamlined (how the module was built deserves a blog post all to itself).  Let’s get started...

The key here is a new standard field named __Thumbnail.  It is in the Appearance section on all items deriving from Standard Template.  This is the “hard way” because that section is not visible within Content Editor by default, so you have to turn on visibility of Standard Fields.  I’ll walk you through the process for a template:

  1. Open Template Manager and navigate to the template you want to provide a thumbnail for.
  2. In the Ribbon, click on the View tab.
  3. In the View chunk, check the checkbox next to “Standard Fields”

    StandardFields
    Standard Fields Checkbox
  4. Switch to the “Content” tab for the template.

    ContentTab
    Content Tab
  5. Scroll down to the “Appearance” section (you may have to expand it) and look for the Thumbnail field.

    ThumbnailField
    Thumbnail Field
  6. Choose an image in this field and it will be displayed in the Insert dialog in the Page Editor.  You might want to use a screenshot from a comp or an actual page on the site instead of the lame wireframe I created for this post – it’s up to you.  Be sure to save your changes to the template!

This same process works for Branch Templates, XSL Renderings, and Sublayouts as well.  Note, to get renderings and sublayouts to appear in the Insert Renderings dialog, you will have to add them to the Allowed Controls on a placeholder settings item.  If you don’t know how that works and want me to post about it too, send me a message on Twitter or post a comment here.

Sitecore’s thumbnail feature is a great usability improvement in recent releases and can really help to guide a content author when making decisions about what type of page to create or which rendering to stick in a given placeholder.  Unfortunately, the field is a little difficult to get to in Sitecore’s default interface.  Tomorrow, I’ll post about a module I have created that adds a “Thumbnail” button to the ribbon to give you a convenient shortcut to assigning thumbnails.

One quick comment to the Sitecore guys regarding the unified Page Editor.  It’s bounds ahead of the old page designer, but I recently had an opportunity to work with Sitefinity 4.  Their drag-and-drop interface is even more intuitive – I would love to see something like that come to Sitecore!

with no comments
Filed under: ,

So the next release of EviBlog for Sitecore is going to be called WeBlog… The new trac page and SVN have been set up - you can see for yourself here http://trac.sitecore.net/WeBlog/

Being the curious soul that I am, I decided to grab the latest code from Subversion and try to get it running on the latest Sitecore 6.5.0 Update-1. The primary difficulty I had was the lack of an up-to-date package ZIP to get the items into a clean Sitecore database (the WeBlog-pre 2.0.0.zip in Subversion was pretty out of date). I powered through and I think I’ve found all of the Sitecore items that needed to be updated. WeBlog does appear to be working, at least. :)


Figure 1 - The WeBlog tab in the Page Editor ribbon

So what’s new? Hard for me to say because I actually haven’t kept up on EviBlog development… So some of these features may be new to me, but not new to WeBlog. The first thing I noticed is the integration with Sitecore’s page editor. There is now a “WeBlog” tab in the ribbon with icons for creating new blogs, creating categories within a blog, or posting a new blog entry (see Figure 1). I only tried this as an admin user, so I don’t know how the security is set up on these buttons - but I imagine it’s possible to hide the new blog button from non-admins. However, restricting who can post to an individual blog doesn’t appear to be included and seems like it would be rather complicated to implement using Sitecore security. Maybe WeBlog needs a field on the Blog template to let you specify a security Role allowed to create new entries and they could check that when the New Entry button is clicked?


Figure 2 - Administration component

The next new thing I noticed was an Administration component at the bottom of the right rail (Figure 2). This only appears while in Edit mode and currently only allows you to modify what theme the blog is using. I couldn’t get this to work at all (changing the value in the drop-down caused the page to post back, but the change wasn’t reflected and it wasn’t possible to click the Save icon in the ribbon). An admin function might be better implemented using an EditFrame with a button to open the field editor. It looks like they’re already doing this with the “Blog” button in the “Settings” chunk in the ribbon.

Speaking of themes, WeBlog comes with a few but they are all just color variations of the same basic theme. It would be nice to see how flexible the mark-up and styling is - hopefully someone good at CSS can produce a really different looking theme to show off the capabilities. As far as how themes are implemented, they appear to just be a Sitecore item with one field pointing to a .CSS file on disk.

Additional new features include CAPTCHA support (with a built-in option as well as reCAPTCHA), and AddThis support for Facebook like buttons, a Tweet button (along with a counter), and the usual AddThis drop down to support other sharing possibilities (I see a sublayout for ShareThis too, but don’t know what it takes to get it working or if it’s global or can be configured per-blog).

In this build, some things definitely don’t work the way I’d expect. When I add a new entry to my blog in page editor using the “New Entry” button in the ribbon, it prompts me for the name of the new entry, but then doesn’t navigate me to the entry so that I can edit it (I suspect this may be due to WeBlog using the NewsMover module to move it into a year/month folder structure maybe). Also, the new entry doesn’t show up in the list of entries on the blog’s main page when it re-loads. If I force a refresh, it shows up and I can click on it to get to the entry and edit it. I already mentioned the non-working Administration component. WeBlog is still pre-release software, so I can’t ask for much… I just wanted to check it out since it’s been a while. It looks really good and I can’t wait to try out the release - hopefully coming soon!

Filed under: , ,

For a while now, I’ve been primarily using Sitecore sublayouts as my rendering-of-choice. The suite of controls available are hard to resist, and it’s easier for someone that doesn’t know XSLT or Sitecore to modify the sublayouts (they’re just user controls, after all). Recently, I’ve started data binding SItecore items to ASP.NET Repeater controls in my sublayouts in a new way using LINQ.

For example, let’s say we have a multilist field on the current item and we want to display a list of the linked items with thumbnails and short descriptions. Here’s how I might now build my Repeater for that:

<asp:Repeater id=“SeeMoreRepeater” runat=“server”>
        <ItemTemplate>
                <sc:Image runat=“server” Item=“<%# Container.DataItem %>” Field=“Thumbnail” />
                <div class=“header”>
                        <sc:Text runat=“server” Item=“<%# Container.DataItem %>” Field=“Title” />
                </div>
                <p>
                        <sc:FieldRenderer runat=“server” Item=“<%# Container.DataItem %>” Field=“ShortDescription” />
                </p>
        </ItemTemplate>
</asp:Repeater>

Code-behind looks like this:

MultiListField seeMoreField = Sitecore.Context.Item.Fields[“SeeMore”];
SeeMoreRepeater.DataSource = seeMoreField.TargetIDs.Select( id => Sitecore.Context.Database.GetItem(id) );
SeeMoreRepeater.DataBind();

This seems like a very clean way to build a Repeater control and a clean way to data bind in code-behind. Additionally, using the <sc:Image>, <sc:Text>, and <sc:FieldRenderer> controls enables the content to be edited when the page is viewed in Sitecore’s page editor.

What do you think?

with 6 comment(s)
Filed under: , ,

It’s spring break and I wasn’t going to make any blog posts this week, but I just ran across this post ( http://www.markvanaalst.com/custom-field/using-jquery-in-custom-fields/ ) from Mark van Aalst concerning using jQuery in Sitecore’s content editor and I just had to comment on it...

I have tried to do this a few times in the past with little to no success. The problem is that, as Mark’s post points out - you need to call $.noConflict() first thing before any other script libraries get loaded. The key that he found in Alexey Rusakov’s FiedTypes module source code (available at http://trac.sitecore.net/FieldTypes/ by the way) was exactly how to do that... You can insert a custom processor into the renderContentEditor pipeline to output the jQuery script first thing. And the truly brilliant part - Alexey is ensuring it is the very first processor in the pipeline by using the new auto-include config file feature in Sitecore 6! (if you haven’t seen that feature before either, check out http://sdn.sitecore.net/faq/administration/how%20to%20auto-include%20configuration%20files.aspx on SDN and be amazed)

Mark’s post didn’t 100% put the pieces together for me, so I’ll spell it out here:

Step 1) Create a pipeline processor class to add jQuery to the page’s <head>. Mark’s post has a great example, so I won’t repeat it here.

Step 2) Create a new .config file in /App_Config/Include. The contents just need to look something like this:

<configuration xmlns:x=“http://www.sitecore.net/xmlconfig/“>
  <sitecore>
    <pipelines>
      <renderContentEditor>
        <processor x:before=“*[1]” type=“Your.Type,Your.Assembly” />
      </renderContentEditor>
    </pipelines>
  </sitecore>
</configuration>

The magic bit is the before=“*[1]”. This is explained in the SDN link above, but in case you can’t get to that - you can specify any XPath query (relative to the attribute’s node’s parent). *[1] represents the first child, so in this instance we are asking Sitecore to insert our new processor before the existing first child of the <renderContentEditor> element. This ensures it runs first before any other processor has a chance to inject a script into the content editor page.

In the past when trying to get fancy with a custom field type, I’ve had to fall back on using prototype and give up fancy stuff like scriptaculous or jquery while working in the content editor. No more! With this technique, you can always apply jQuery’s $.noConflict() early on and not worry about some other library calling the wrong $() implementation and causing weird errors. Thank you, Mark! And thank you Alexey for sharing your source!

For the last few weeks, I have been working on a project to customize Able Commerce 7.0.4. When I started on this project, I had never seen Able before and certainly had no idea about the right way to go about doing some of these customizations. Over the course of the project, I’ve figured out a few “best practices” and I figured I’d share them with my blog readers. If you’re reading this and have worked with Able before, feel free to share any other thoughts you may have or correct me if there’s an even better way. :)

The first step in customization is to create a new site theme to contain your work. Note - this is important even if you don’t plan to customize the colors, graphics, and layout of the site. To create a new theme, log in as an admin and navigate to the “Themes File Manager” page on the “Website” menu.

Able01.l5UWbEhSx35A.jpg

(click for larger image)

In the lower-left corner of this screen is a box to create a new theme by copying an existing one. For this discussion, I have copied the YellowJacket theme and created a new one called “MyTheme”.

Able02.H5sM8AtnVCz2.jpg

This theme will act as a sort of “container” for your customizations and allow you or your end users to easily activate and deactivate them through the Able UI.

Next, you’ll need to copy a clean Scriptlets folder into your theme (I don’t know why copying an existing theme doesn’t just do this for you... *shrug*). In a standard Able Commerce 7.0.4 install, the Scriptlets folder is located in the App_Data folder under the root of the site. Copy it into the root of your theme’s folder (in my case, I copied /App_Data/Scriptlets to /App_Themes/MyTheme/Scriptlets). If you look inside this folder, you’ll see a Default folder (containing all of the default scriptlets that come with Able) and a Custom folder (containing a structure of empty folders). If we copy a scriptlet inside of the Custom folder, Able will automatically use it in place of the default one. In fact, this is done automatically for you when you customize a scriptlet in Able’s admin interface. But I’ll get to that in a bit.

Now - to actually customize something... Here’s something simple - by default, the Contact Us page just displays some contact information. I’ll add a form to allow the user to submit feedback through the website.

First, I created a new ASP.NET user control - ContactForm.ascx and put it in the /ConLib/Custom folder. This spot seems to exist specifically for custom controls like this. Previously, I have copied controls that come with Able (from the parent ConLib folder) here to make minor changes to them as well.

My ContactForm.ascx is very simple - a few text boxes, a few labels, and a submit button plus code to send an e-mail to an address that is specified via a property. You would probably want to fancy it up a bit with some validation controls, error handling, etc. If you want to see the code I’ve created though, you can download it here: http://www.kevinwilliams.name:8000/dotNetAndStuff/AbleContactForm.zip. The Able Commerce magic comes from this comment at the top of the .ascx:

<%--

<conlib>

<summary>

Displays a simple “contact us” or feedback form on the site that allows the user to type a message

and have it submitted to the site’s owner via e-mail.

</summary>

<param name=“EmailTo” default=“admin@example.com”>

Specifies the e-mail address you want the contents of the form submitted to.

</param>

</conlib>

--%>

This is the help text that will be displayed inside Able Commerce for this control. To see it, log in to the Able admin and choose the “ConLib Reference” option from the “Help” menu. In the “Select Control” drop-down, scroll to the bottom and select Custom\ContactForm and the help for the control will be displayed. Note, this also includes a “Usage” that looks like this:

[[ConLib:Custom\ContactForm EmailTo=“admin@example.com”]]

This is how the new control is placed onto a scriptlet - which happens to be the last step I need to do to make the contact form appear on the site. In the Able admin, choose the “Content and Layout” option from the “Website” menu. In the “Show Scriptlets From Theme” drop-down, choose the theme being customized (in my case, “MyTheme”). Able will present a list of all scriptlets for that theme. Scrolling down a bit, I see the “Contact Us” scriptlet. To the right of each scriptlet there is an “Edit” button (the icon looks like a notebook with a pencil). Clicking this brings up the screen used to customize a scriptlet:

Able03.n4rhGVQY1GtO.jpg

(click to enlarge)

If you look at this screenshot, I have basically just copy/pasted the example usage from the help to the bottom of the existing scriptlet content. Clicking the “Save” button takes me back to the list of scriptlets. But when I scroll down to “Contact Us”, I can now see an X in the “Custom” column - indicating that I have customized this scriptlet.

Anyhow - that’s it! When I log out of the admin interface and go to the Contact Us page on the site, I can see and use my new contact form.

The practices I’ve mentioned here (place your customizations inside a custom theme, use the Able interface to customize scriptlets, add the XML comments to your custom user controls, and placing your user controls in the ConLib\Custom folder) should make it easier to upgrade your site to new releases of Able Commerce, make it easier for you to share your customizations with other Able users, make your work easier to maintain, and make it easy to enable or disable your customizations through the Able admin interface. Have fun!

Filed under:

Mark van Aalst’s EviBlog shared source module takes advantage of the new RSS feed features added to Sitecore in version 6.2, but if you haven’t set that up yet (I hadn’t!) then you may be wondering how to make it work. It’s actually quite simple!

In Content Editor, navigate in the tree to where you want your RSS feed URL to live. Add a content item using the /System/Feeds/RSS Feed data template and name it as you want it to appear in the URL. In my case, I wanted all of my site’s RSS feeds to have URLs such as http://www.kevinwilliams.name:8001/RSS/BlogName.aspx so I created a folder named RSS and created an RSS Feed matching my blog name (in this example, I used “TestBlog”). Heres a screenshot:

Screenshot2010-03-07at1.03.43AM.iUv4jNJOT7M5.jpg

When setting up a Sitecore 6.2 RSS feed, the “Items” field defines what items will be included in the feed. You can either use a Sitecore query ( like query:/sitecore/content/Home/TestBlog/*[@@templatekey=‘blogentry’] ) or simply provide the path to an item whose children should be included in the feed like I did above. Enter a title and description for your blog, and that’s it! EviBlog is already configured with the proper Feed device settings for Sitecore 6.2, so this should be all you have to do. Publish changes and hit your URL to see the RSS feed for your blog! My test one is at http://www.kevinwilliams.name:8001/RSS/TestBlog.aspx.

Something that doesn’t come with EviBlog, but I wanted to add to my site was a “Subscribe via RSS” link on my blog page. To do this, I created a new XSLT Rendering called “RSS Link” and gave it a parameter to control the text that appears like this:

<xsl:param name="Link_Text">Subscribe via RSS</xsl:param>

Then I made the body of the template look like this:

<xsl:template match="*" mode="main">
<sc:link type="application/rss+xml">
<img src="/sitecore/shell/Themes/Standard/Custom/16x16/rss.png" alt="RSS Icon" border="0"/>
<xsl:value-of select="$Link_Text"/>
</sc:link>

</xsl:template>

I want this link to appear in my blog’s sidebar, but the renderings allowed there are restricted. To allow my rendering, I opened the content editor to /sitecore/Layout/Placeholder Settings/phBlogSidebar and added the “RSS Link” rendering to the Allowed Controls.

Lastly, I opened the main blog content item in the page designer, selected the “phBlogSidebar” placeholder, and added my RSS Link rendering. I opened the properties for the rendering and set the Data Source to the RSS feed I created earlier, clicked OK, and clicked Save in the designer. The “Subscribe via RSS” link now appears in my blog’s sidebar!

The only thing left to do would be add support for auto-discovery of my RSS feed (for aggregators like Google Reader). It’s rather simple ( there is an article about it here: http://www.petefreitag.com/item/384.cfm ), but requires inserting some HTML into your page’s <head> tag. One idea for doing this would be to re-implement the above RSS Link rendering as a sublayout instead and then add the HTML to Page.Header.Controls in the ASCX file’s Page_Load() or something. I’ll leave that as an exercise to the reader. :)

EviBlog makes good use of Sitecore 6.2’s new RSS feeds feature and therefore it is very easy to add RSS to your blog. If you’d like more information about the new RSS feed functionality in Sitecore, take a look at the Presentation Component Cookbook on SDN or feel free to contact me if you need any help!

with 1 comment(s)
Filed under: , ,
More Posts Next page »