SQL Plus Linux

So, I had a couple of hours today to play around with something and I had recently seen the news about public availability of the SQL Server on Linux preview... Short version - it seems to work with Sitecore. Of course I would never use this in a real environment (probably not even a development environment), but it is encouraging to see that all of the basic SQL Server features required by Sitecore seem to be working already. I'm kind of excited for this to get to a level that I can trust it for production sites (I really love Linux), so I'll definitely be keeping an eye on it.

Now - if you want to see this for yourself, the quickest way is to just spin up a Linux virtual machine. I downloaded Ubuntu Linux Server version 16.04.1 LTS (the LTS stands for Long Term Support) which is the version recommended by Microsoft. I used VMWare Player on my machine to create the VM and it pretty much fully automated the OS installation. Just be sure to use bridged networking if you want to access your VM from outside of the host machine (with VMware at least, the default is NAT). Your mileage may vary with other virtualization products like HyperV or Virtualbox, but Ubuntu makes installation relatively painless if you follow the on-screen prompts.

Once the VM was up and running and I was logged in to a prompt, I just followed the instructions here https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-ubuntu and... Well, it was that easy. I could connect to SQL Server from a Windows machine using SQL Server Management Studio and the sa credentials I created during installation.

Getting the Sitecore databases attached was actually really easy too. If you've done this in a Windows version of SQL Server, the process is basically the same. You just need to get the .MDF and .LDF files onto the Linux machine. For this, I installed openssh-server (from the prompt on the Ubuntu box, I typed "sudo apt-get install openssh-server") and used the scp command from the Git Bash prompt on my Windows machine (or you could use pscp.exe that comes with Putty if you prefer). There are lots of ways to get files in and out of a Linux server, but this was quick and easy for me given the tools I had at hand. So anyway, I used scp to copy the files into my home directory on the Linux server then moved those files to /var/opt/mssql/data and changed their owner to the mssql user (command was "chown mssql.mssql Sitecore*"). Then in SQL Server Management Studio, I attached the databases as per normal (note that the root of the Linux filesystem is considered C:\ by Management Studio, so my files were in C:\var\opt\mssql\data).

Then I just pointed my Sitecore ConnectionStrings.config at those databases on that server like I normally would and... Everything seemed to work!

The current SQL Server for Linux preview isn't optimized for performance and I'm sure it will be a while before we have some best practices for configuring and deploying it in production environments. And although everything I tried in Sitecore appeared to work fine, there's still a chance some SQL Server feature that doesn't yet work in Linux is required by some bit of Sitecore somewhere. So I wouldn't use this for anything serious, but it was a fun experiment just to see what would happen!

Filed under: ,

One of the SEO best practices everyone should trying to implement on their sites relates to URLs with spaces. I’m sure you’ve seen a URL with an encoded space before - %20. Yuck. Not only does it look bad to your site’s visitors, but search engines don’t particularly like it either. From what I’ve read (e.g. http://goo.gl/mwNpgP), crawlers like Google definitely prefer to see dashes where humans like to see spaces.

I’ve seen a few recommendations for how to deal with this in Sitecore before. If you’re currently using encodeNameReplacements, InvalidItemNameChars, and/or ItemNameValidation - I think I have discovered a better way...

Sitecore generates URLs for items using the name of the item (and its parent items in the content tree), therefore what we really need is for items to be named something like “my-blog-post” instead of “My Blog Post”. Having content authors follow a naming convention is probably too much to ask, and what if they forget? Plus, it’s not particularly friendly for them to look at in the Sitecore content tree either.

What if we could let them name the item however they want and let them continue to see the name they entered in the content tree, but actually have the item named in an SEO friendly way in the database? What mechanism does Sitecore give us to allow an item to be named one thing, but display using a different name in the content tree? Perhaps you’ve seen or used the Display Name before?


Display Name in the Sitecore Ribbon

The essence of my solution to the spaces in URLs problem is to handle the Sitecore events for an item being added or renamed, detect items with a space in their name, rename them to use a dash in place of a space, and copy the original name into the Display Name so that content authors don’t see what we’ve done.

Here’s the complete code for a solution that I’m using on my current project. It makes use of a technique that I mentioned in a previous blog post (see Subscribing to Sitecore Events in a PreApplicationStartMethod) to subscribe to the item:added and item:renamed events, and then performs the above logic. I also threw in a .ToLower() on the item name while I’m at it because apparently search engines prefer to see all lowercase URLs (note - I’m also using the lowercaseUrls attribute on the linkManager, so this shouldn’t really be necessary - up to you if you want to include it in your solution or not).

Oh - that regex you see in there takes care of cases where an item was named with spaces surrounding a dash. Otherwise “Account - Change Password” would become “account---change-password”.


using System;
using System.Text.RegularExpressions;
using System.Web;

using Sitecore.Data.Items;
using Sitecore.Events;

[assembly: PreApplicationStartMethod(typeof([Namespace].[Class]), "Start")]

namespace [Namespace]
{
    /// 
    /// Sitecore item:added and item:renamed event handler that ensures
    /// item names are created with dashes instead of spaces and that
    /// the original name (with spaces) is stored in Display Name.
    /// 
    public class [Class]
    {
        /// 
        /// Start method that registers the Sitecore event handlers.
        /// 
        public static void Start()
        {
            var handler = new [Class]();
            Event.Subscribe("item:added", new EventHandler(handler.OnEvent));
            Event.Subscribe("item:renamed", new EventHandler(handler.OnEvent));
        }

        /// 
        /// Called when a Sitecore item is saved or renamed.
        /// 
        public void OnEvent(object sender, EventArgs args)
        {
            // Pull the item from the args.
            var item = (Item)Event.ExtractParameter(args, 0);

            // Do nothing if the item isn't in the master database.
            if (item.Database.Name != "master") return;

            // We're only concerned with items under /sitecore/content
            // (so not Layouts or Templates or whatever).
            if (!item.Paths.Path.StartsWith("/sitecore/content")) return;

            // If spaces are found in the name, replace them.
            string newName;
            if (item.Name == (newName = Regex.Replace(item.Name.ToLower().Replace(' ', '-'), "-{2,}", "-")))
            {
                // If no spaces were found, do nothing.
                return;
            }

            // Edit the item that was just saved.
            item.Editing.BeginEdit();
            try
            {
                // Set the display name to the original name (with spaces).
                item.Appearance.DisplayName = item.Name;

                // Set the item name to the new name (with dashes).
                item.Name = newName;
            }
            finally
            {
                item.Editing.EndEdit();
            }
        }
    }
}

I can’t take full credit for this solution as one of my Hanson Dodge Creative co-workers originally proposed the idea (and I think similar code was already in use on another client’s site), but this particular implementation is mostly mine. In the future, I would like to improve this solution in some ways - perhaps implement it as a rules engine action to plug into something like John West mentions in his post here: http://goo.gl/7UAm5W. If you have any suggestions, please pass them along! I can be reached on Twitter @williamsk000 or you can leave a comment here.

Filed under:

PreApplicationStartMethod is a neat .NET 4.0 trick that I only recently discovered. It allows you to have code in your assembly run during application startup. As Sitecore / ASP.NET developers, we can think of this like adding code to Application_Start in global.asax, only it’s in your assembly (no global.asax modifications needed). One thing this is commonly used for is to perform dependency injection, for example.

On the same day that I discovered PreApplicationStartMethod, I also found out that Sitecore events can be subscribed to at runtime rather than having to configure them via a .config file. When you combine these two techniques, you get a way to ensure that event handlers in your assemblies get wired up without requiring anything more than dropping your DLL in the bin folder.

Here’s a more concrete example… Let’s say I wanted to distribute a DLL that performs some action whenever an item is saved - maybe update a cache or write to an audit log or something. Your class might look something like this:

namespace [Namespace]
{
  public class [Class]
  {
    public void OnItemSaved(object sender, EventArgs args)
    {
      // Extract the item.
      var item = Sitecore.Events.Event.ExtractParameter(args, 0) as Item;

      // Do something with the item...
      // cache.Update(item);
      // Log.Audit(“Item updated: “ + item.ID);
    }
  }
}

Then, you ordinarily would throw something like this into a .config file in App_Config\Include:

<events>
  <event name=”item:saved”>
    <handler type=”[Namespace].[Class], [Assembly]” method=”OnItemSaved” />
  </event>
</events>

But with the addition of a PreApplicationStartMethod, you can avoid the .config file altogether. In order to activate your event handler, you’d just throw it in the bin folder of your Sitecore site. How is this done? First, throw an application level attribute into your class’ source file:

[assembly: PreApplicationStartMethod(typeof([Namespace].[Class]), "OnStart")]

Then, add the “OnStart” method to your class:

    public static void OnStart()
    {
      var handler = new [Class]();
      Sitecore.Events.Event.Subscribe(“item:saved”, new EventHandler(handler.OnItemSaved);
    }

And that’s it! Now if you compile and drop your DLL into the bin folder, your event handler will run each time an item is saved, and no further configuration is needed!

The PreApplicationStartMethod was introduced in .NET 4.0 and apparently had some limitations (only one of them could exist per assembly was the big one), but as far as I know those limitations have been entirely removed with .NET 4.5. If you’re using Sitecore 7+, you’re already on .NET 4.5, so… Run with it! If you have some other creative uses for either that, or runtime subscription to Sitecore events - let me know about it.

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: , ,
More Posts Next page »