Home » Sitecore (Page 5)
Category Archives: Sitecore
Bucket Items in Sitecore using a Custom Commandlet in Sitecore PowerShell Extensions
Last Wednesday I had the privilege to present Sitecore PowerShell Extensions (SPE) at the Milwaukee Sitecore Meetup. During my presentation, I demonstrated how easy it is to add, execute and reuse PowerShell scripts in SPE, and I showcased version 3.0 of SPE on Sitecore XP 8.
Unfortunately, I ran out of time before showing how one can go about creating a custom commandlet in SPE, and hope to make it up to everyone by sharing the commandlet I wrote for the presentation in this post.
I wrote the following commandlet to convert an Item into an Item Bucket in Sitecore:
using System;
using System.Management.Automation;
using Sitecore.Data.Items;
using Sitecore.Shell.Framework.Commands;
using Cognifide.PowerShell.Commandlets;
using Cognifide.PowerShell.Commandlets.Interactive.Messages;
namespace Sitecore.Sandbox.SPE.Commandlets.Buckets
{
[Cmdlet(VerbsData.ConvertTo, "Bucket"), OutputType(new Type[] { typeof(Item) })]
public class ConvertToBucketCommand : BaseItemCommand
{
protected override void ProcessItem(Item item)
{
try
{
PutMessage(new ShellCommandInItemContextMessage(item, "item:bucket"));
}
catch (Exception exception)
{
WriteError(new ErrorRecord(exception, "sitecore_new_bucket_error", ErrorCategory.NotSpecified, Item));
}
WriteItem(Item);
}
}
}
The above commandlet implements the ProcessItem() method — this method is declared abstract in one of the ancestor classes of the class above — and leverages the framework of SPE to invoke a Sheer UI command to bucket the Item passed to the method — one of the ancestor classes of this class passes the Item to be processed.
The above highlights how in SPE we are employing the Template method pattern for many “out of the box” commandlets. This involves inheriting from an abstract base class — Cognifide.PowerShell.Commandlets.BaseItemCommand in Cognifide.PowerShell.dll (this assembly comes with the SPE module) is an example of one of these base classes — and implementing methods that are defined as abstract. The parent or an ancestor class will do the brunt of the work behind the scenes, and use your method implementation for specifics.
As a side note, we also provide method hooks as well — these are virtual methods defined on a base or ancestor class — which you can override to change how they work to meet your particular needs.
I then wired the above up using a Sitecore include configuration file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<powershell>
<commandlets>
<add Name="Custom Bucket Commandlets" type="*, Sitecore.Sandbox.SPE" />
</commandlets>
</powershell>
</sitecore>
</configuration>
I deployed the above to my Sitecore instance; loaded up the Integrated Scripting Environment (ISE) in SPE; and saw that my commandlet was registered using the Control-Space shortcut key:
Let’s take this for a spin. Let’s convert the Home Item into an Item Bucket:
Here’s my script to do that:
I clicked the execute button, and then got this confirmation dialog:
I then clicked the “Ok” button and was immediately presented with this dialog:
As you can see it worked! The Home Item in my content tree is now an Item Bucket:
If you have any thoughts on this or ideas for other custom commandlets for SPE, please share in a comment.
If you would like to watch the Milwaukee Sitecore Meetup presentation where I showcased Sitecore PowerShell Extensions — and as a bonus you’ll also get to see some real-life application of SPE from Adam Brauer, Senior Product Engineer at Active Commerce, in this presentation as well — it has been recorded for posterity, and you can watch it here:
Until next time, stay curious, keep experimenting, and let’s keep on sharing all the Sitecore things!
Warn Content Authors on Having Too Many Sub-items Under an Item in Sitecore
In my previous post, I shared two field validators that will warn content authors/editors when they link to Items without presentation in Internal and General Link fields.
When I was building those two validators, I came up with another validator idea: how about warning content authors/editors when they have too many sub-items under an Item?
To accomplish this, I came up with the following class that serves as an Item validator:
using System;
using System.Runtime.Serialization;
using Sitecore.Buckets.Managers;
using Sitecore.Data.Items;
using Sitecore.Data.Validators;
namespace Sitecore.Sandbox.Data.Validators.ItemValidators
{
[Serializable]
public class ItemHasTooManySubitemsValidator : StandardValidator
{
public override string Name
{
get
{
return Parameters["Name"];
}
}
private int MaxNumberOfSubitems
{
get
{
int maxNumberOfSubitems;
if (!int.TryParse(Parameters["MaxNumberOfSubitems"], out maxNumberOfSubitems))
{
return 0;
}
return maxNumberOfSubitems;
}
}
public ItemHasTooManySubitemsValidator()
{
}
public ItemHasTooManySubitemsValidator(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
protected override ValidatorResult Evaluate()
{
Item item = GetItem();
if(IsValid(item))
{
return ValidatorResult.Valid;
}
Text = GetErrorMessage(item);
return GetFailedResult(ValidatorResult.Suggestion);
}
protected virtual bool IsValid(Item item)
{
return MaxNumberOfSubitems < 1
|| item == null
|| IsBucket(item)
|| !item.HasChildren
|| item.Children.Count <= MaxNumberOfSubitems;
}
protected virtual bool IsBucket(Item item)
{
if(item == null)
{
return false;
}
return BucketManager.IsBucket(item);
}
protected virtual string GetErrorMessage(Item item)
{
string message = Parameters["ErrorMessage"];
if (string.IsNullOrWhiteSpace(message))
{
return string.Empty;
}
return GetText(message, new[] { item.DisplayName });
}
protected override ValidatorResult GetMaxValidatorResult()
{
return base.GetFailedResult(ValidatorResult.Suggestion);
}
}
}
The class above inherits from Sitecore.Data.Validators.StandardValidator in Sitecore.Kernel.dll — this is the base class which most validators in Sitecore inherit from — and ascertains whether the Item being validated has too many sub-items underneath it (the maximum number of allowed sub-items is passed to the class’ instance via the MaxNumberOfSubitems parameter set on the Validation Rule item — these have the /sitecore/templates/System/Validation/Validation Rule template — in Sitecore which is shown later in this post).
If the Item being validated has more sub-items than is allowed and isn’t an Item Bucket, the validator’s error message is set on the Text property of the class instance — the error message is passed via a parameter on the Validation Rule item — and a ValidatorResult instance is returned to the caller.
I then wired up the above class in Sitecore on a Validation Rule item, and set the maximum number of allowed sub-items to be four for testing (no, I’m not going to create a gazillion Items to test this):
Now that we have the Validation Rule Item in place, we should probably give content authors/editors the ability to remedy having too many sub-items under an Item.
How?
Let’s give them the ability to convert the Item into an Item Bucket. I created the following Menu item — this has the template of /sitecore/templates/System/Menus/Menu item — to empower content authors/editors on making this conversion:
I then had to set up my Sample Item template to be bucketable since we are giving the ability to bucket Items with this template:
I then mapped the Item validator to the Standard Values item of my Sample Item template:
For testing, I created some Items underneath another Item:
As you can see, we haven’t exceeded the maximum number of 4 quite yet.
I then created a fifth item, and was presented with a validation warning:
I right clicked on the square in the Validation Bar, and was presented with some options:
I clicked on “Convert to Item Bucket”, and then saw a magical progress dialog followed by this:
If you have any thoughts on this, or ideas for other Item validators, please drop a comment.
Warn Content Authors of Linking to Items With No Presentation using Custom Field Validators in Sitecore
The other day John West, CTO of Sitecore USA, published his 500th blog post — quite an epic feat if you ask me — where he built a custom field validator that checks whether external links in the Rich Text field resolve:
This got me thinking: what other types of field validators might be useful?
I pondered over this for the past couple of days, and couldn’t think of anything useful but finally did come up with an idea this morning (out of the blue I might add): how about field validators that check to see whether Items linked in General and Internal Link fields have presentation?
After searching through the library of field validators available in Sitecore — I did this to make sure I wouldn’t be wasting my time given that Sitecore offers a lot of field validators “out of the box” (these live under /sitecore/system/Settings/Validation Rules/Field Rules in the master database), so I suggest having a look through these before building a custom one — I came up with the following solution that employs the Template method design pattern:
using System;
using System.Runtime.Serialization;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Data.Validators;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.HasPresentation;
namespace Sitecore.Sandbox.Data.Validators.FieldValidators
{
public abstract class ReferencedItemHasPresentationValidator : StandardValidator
{
public override string Name
{
get
{
return Parameters["Name"];
}
}
public ReferencedItemHasPresentationValidator()
{
}
public ReferencedItemHasPresentationValidator(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
protected override ValidatorResult Evaluate()
{
Item linkedItem = GetReferencedItem();
if (linkedItem == null || HasPresentation(linkedItem))
{
return ValidatorResult.Valid;
}
Text = GetErrorMessage();
return GetFailedResult(ValidatorResult.Error);
}
protected virtual bool HasPresentation(Item item)
{
Assert.ArgumentNotNull(item, "item");
return HasPresentationPipeline.Run(item);
}
protected abstract Item GetReferencedItem();
protected virtual string GetErrorMessage()
{
string message = Parameters["ErrorMessage"];
if (string.IsNullOrWhiteSpace(message))
{
return string.Empty;
}
return GetText(ExpandTokens(message), new[] { GetFieldDisplayName() });
}
protected override ValidatorResult GetMaxValidatorResult()
{
return GetFailedResult(ValidatorResult.Error);
}
protected virtual string ExpandTokens(string value)
{
if(string.IsNullOrWhiteSpace(value))
{
return value;
}
string valueExpanded = value;
Field field = GetField();
if(field != null)
{
valueExpanded = valueExpanded.Replace("$fieldName", field.Name);
}
return valueExpanded;
}
}
}
The above abstract class inherits from Sitecore.Data.Validators.StandardValidator in Sitecore.Kernel.dll — this is the base class which most validators in Sitecore inherit from — and checks to see if the Item referenced in the field has presentation (this check is done in the HasPresentation() method which basically delegates to the Run() method on the Sitecore.Pipelines.HasPresentation.HasPresentationPipeline class).
The referenced Item is returned by the GetReferencedItem() method which must be defined by subclasses of the above class.
Further, I’m passing in the validator’s name and error message through parameters (the error message allows for $fieldName as a token, and the ExpandTokens() method replaces this token with the name of the field being validated).
I then created a subclass of the above to return the Item referenced in an Internal Link field:
using System;
using System.Runtime.Serialization;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
namespace Sitecore.Sandbox.Data.Validators.FieldValidators
{
[Serializable]
public class InternalLinkItemHasPresentationValidator : ReferencedItemHasPresentationValidator
{
public InternalLinkItemHasPresentationValidator()
{
}
public InternalLinkItemHasPresentationValidator(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
protected override Item GetReferencedItem()
{
InternalLinkField internalLinkField = GetField();
if (internalLinkField == null)
{
return null;
}
return internalLinkField.TargetItem;
}
}
}
Nothing magical is happening in the above class. The GetReferencedItem() method is casting the field to a Sitecore.Data.Fields.InternalLinkField instance, and returns the value of its TargetItem property.
Now that we have a class to handle Items referenced in Internal Link fields, we need another for General Link fields:
using System;
using System.Runtime.Serialization;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
namespace Sitecore.Sandbox.Data.Validators.FieldValidators
{
[Serializable]
public class GeneralLinkItemHasPresentationValidator : ReferencedItemHasPresentationValidator
{
public GeneralLinkItemHasPresentationValidator()
{
}
public GeneralLinkItemHasPresentationValidator(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
protected override Item GetReferencedItem()
{
LinkField linkField = GetField();
if (linkField == null)
{
return null;
}
return linkField.TargetItem;
}
}
}
The GetReferencedItem() method in the GeneralLinkItemHasPresentationValidator class above does virtually the same thing as the same method in the InternalLinkItemHasPresentationValidator class. The only difference is the GetReferencedItem() method in the class above is casting the field to a Sitecore.Data.Fields.LinkField instance, and returns the value of its TargetItem property.
I then had to map the above field validator classes to Validation Rule Items — these have the /sitecore/templates/System/Validation/Validation Rule template — in Sitecore:
The Internal Link field validator:
The General Link field validator:
I then added two Internal Link fields on my Sample item template, and mapped the Internal Link field validator to them:
I also created two General Link fields on my Sample item template, and mapped the General Link field validator to them:
Once I had the validators mapped to their specific fields, I went ahead and removed presentation from one of my test items:
Before, the above item had these presentation components on it:
I then linked to my test items in my test fields. As you can see, there are errors on the “Bing” item which does not have presentation:
If you have any thoughts on this, or ideas for other field validators, please share in a comment.
Until next time, have a Sitecorelicious day! 😀
Augment Configuration-defined Sitecore Functionality using the Composite Design Pattern
In a couple of my past posts — Synchronize IDTable Entries Across Multiple Sitecore Databases Using a Composite IDTableProvider and
Chain Together Sitecore Client Commands using a Composite Command — I used the Composite design pattern to chain together functionality in two or more classes with the same interface — by interface here, I don’t strictly mean a C# interface but any class that servers as a base class for others where all instances share the same methods — and had a thought: how could I go about making a generic solution to chain together any class type defined in Sitecore configuration?
As a “proof of concept” I came up with the following solution while taking a break from my judgely duties reviewing 2015 Sitecore Hackathon modules.
I first defined an interface for classes that will construct objects using the Sitecore Configuration Factory:
using System.Collections.Generic;
using System.Xml;
namespace Sitecore.Sandbox.Shared
{
public interface IConfigurationFactoryInstances<TInstance>
{
void AddInstance(XmlNode source);
IEnumerable<TInstance> GetInstances();
}
}
The following class implements the methods defined in the interface above:
using System.Collections.Generic;
using System.Xml;
using Sitecore.Configuration;
namespace Sitecore.Sandbox.Shared
{
public class ConfigurationFactoryInstances<TInstance> : IConfigurationFactoryInstances<TInstance> where TInstance : class
{
private IList<TInstance> Instances { get; set; }
public ConfigurationFactoryInstances()
{
Instances = new List<TInstance>();
}
public virtual IEnumerable<TInstance> GetInstances()
{
return Instances;
}
public void AddInstance(XmlNode configNode)
{
TInstance instance = CreateInstance(configNode);
if (instance == null)
{
return;
}
Instances.Add(instance);
}
protected virtual TInstance CreateInstance(XmlNode configNode)
{
if (configNode == null)
{
return null;
}
TInstance instance = Factory.CreateObject(configNode, true) as TInstance;
if (instance == null)
{
return null;
}
return instance;
}
}
}
The AddInstance() method in the class above — along with the help of the CreateInstance() method — takes in a System.Xml.XmlNode instance and attempts to create an instance of the type denoted by TInstance using the Sitecore Configuration Factory. If the instance was successfully created (i.e. it’s not null), it is added to a list.
The GetInstances() method in the class above just returns the list of the instances that were added by the AddInstance() method.
Since I’ve been posting a lot of meme images on Twitter lately — you can see the evidence here — I’ve decided to have a little fun tonight with this “proof of concept”, and created the following composite MediaProvider:
using System.Xml;
using Sitecore.Data.Items;
using Sitecore.Resources.Media;
using Sitecore.Sandbox.Shared;
namespace Sitecore.Sandbox.Resources.Media
{
public class CompositeMediaProvider : MediaProvider
{
private static IConfigurationFactoryInstances<MediaProvider> Instances { get; set; }
static CompositeMediaProvider()
{
Instances = new ConfigurationFactoryInstances<MediaProvider>();
}
public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
{
foreach (MediaProvider mediaProvider in Instances.GetInstances())
{
string url = mediaProvider.GetMediaUrl(item, options);
if(!string.IsNullOrWhiteSpace(url))
{
return url;
}
}
return base.GetMediaUrl(item, options);
}
protected virtual void AddMediaProvider(XmlNode configNode)
{
Instances.AddInstance(configNode);
}
}
}
The AddMediaProvider() method in the class above adds new instances of Sitecore.Resources.Media.MediaProvider through delegation to an instance of the ConfigurationFactoryInstances class. The Sitecore Configuration Factory will call the AddMediaProvider() method since it’s defined in the patch include configuration file shown later in this post
The GetMediaUrl() method iterates over all instances of Sitecore.Resources.Media.MediaProvider that were created and stored by the ConfigurationFactoryInstances instance, and calls each of their GetMediaUrl() methods. The first non-null or empty URL from one of these “inner” instances is returned to the caller. If none of the instances return a URL, then the class above returns the value given by its base class’ GetMediaUrl() method.
I then spun up three MediaProvider classes to serve up specific image URLs of John West — I found these somewhere on the internet 😉 — when they encounter media Items with specific names (I am not advocating that anyone hard-codes anything like this — these classes are only here to serve as examples):
using System;
using Sitecore.Data.Items;
using Sitecore.Resources.Media;
namespace Sitecore.Sandbox.Resources.Media
{
public class JohnWestOneMediaProvider : MediaProvider
{
public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
{
if(item.Name.Equals("john-west-1", StringComparison.CurrentCultureIgnoreCase))
{
return "http://cdn.meme.am/instances/500x/43030540.jpg";
}
return string.Empty;
}
}
}
using System;
using Sitecore.Data.Items;
using Sitecore.Resources.Media;
namespace Sitecore.Sandbox.Resources.Media
{
public class JohnWestTwoMediaProvider : MediaProvider
{
public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
{
if (item.Name.Equals("john-west-2", StringComparison.CurrentCultureIgnoreCase))
{
return "http://cdn.meme.am/instances/500x/43044627.jpg";
}
return string.Empty;
}
}
}
using System;
using Sitecore.Data.Items;
using Sitecore.Resources.Media;
namespace Sitecore.Sandbox.Resources.Media
{
public class JohnWestThreeMediaProvider : MediaProvider
{
public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
{
if (item.Name.Equals("john-west-3", StringComparison.CurrentCultureIgnoreCase))
{
return "http://cdn.meme.am/instances/500x/43030625.jpg";
}
return string.Empty;
}
}
}
I then registered all of the above in Sitecore using a patch configuration file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<mediaLibrary>
<mediaProvider>
<patch:attribute name="type">Sitecore.Sandbox.Resources.Media.CompositeMediaProvider, Sitecore.Sandbox</patch:attribute>
<mediaProviders hint="raw:AddMediaProvider">
<mediaProvider type="Sitecore.Sandbox.Resources.Media.JohnWestOneMediaProvider, Sitecore.Sandbox" />
<mediaProvider type="Sitecore.Sandbox.Resources.Media.JohnWestTwoMediaProvider, Sitecore.Sandbox" />
<mediaProvider type="Sitecore.Sandbox.Resources.Media.JohnWestThreeMediaProvider, Sitecore.Sandbox" />
<mediaProvider type="Sitecore.Resources.Media.MediaProvider, Sitecore.Kernel" />
</mediaProviders>
</mediaProvider>
</mediaLibrary>
</sitecore>
</configuration>
Let’s see this in action!
To test, I uploaded four identical photos of John West to the Media Library:
I then inserted these into a Rich Text field on my home Item:
I saved my home Item and published everything. Once the publish was finished, I navigated to my home page and saw the following:
As you can see, the three custom John West MediaProvider class instances served up their URLs, and the “out of the box” Sitecore.Resources.Media.MediaProvider instance served up its URL on the last image.
If you have any thoughts on this, please share in a comment.
Until next time, have a Sitecoretastic day!
2014 Blog Statistics
The WordPress.com stats helper monkeys prepared a 2014 annual report for this blog.
Here’s an excerpt:
The concert hall at the Sydney Opera House holds 2,700 people. This blog was viewed about 45,000 times in 2014. If it were a concert at Sydney Opera House, it would take about 17 sold-out performances for that many people to see it.
Add Additional Item Fields to RSS Feeds Generated by Sitecore
Earlier today a colleague had asked me how to add additional Item fields into RSS feeds generated by Sitecore.
I could have sworn there was an easy way to do this, but when looking at the RSS Feed Design dialog in Sitecore, it appears you are limited to three fields on your Items “out of the box”:
After a lot of digging in Sitecore.Kernel.dll, I discovered that one can inject a subclass of Sitecore.Syndication.PublicFeed to override/augment RSS feed functionality:
As a proof-of-concept, I whipped up the following subclass of Sitecore.Syndication.PublicFeed:
using System.ServiceModel.Syndication; // Note: you must reference System.ServiceModel.dll to use this!
using Sitecore.Diagnostics;
using Sitecore.Configuration;
using Sitecore.Data.Items;
using Sitecore.Pipelines;
using Sitecore.Pipelines.RenderField;
using Sitecore.Syndication;
namespace Sitecore.Sandbox.Syndication
{
public class ImageInContentPublicFeed : PublicFeed
{
private static string ImageFieldName { get; set; }
private static string RenderFieldPipelineName { get; set; }
static ImageInContentPublicFeed()
{
ImageFieldName = Settings.GetSetting("RSS.Fields.ImageFieldName");
RenderFieldPipelineName = Settings.GetSetting("Pipelines.RenderField");
}
protected override SyndicationItem RenderItem(Item item)
{
SyndicationItem syndicationItem = base.RenderItem(item);
AddImageHtmlToContent(syndicationItem, GetImageFieldHtml(item));
return syndicationItem;
}
protected virtual string GetImageFieldHtml(Item item)
{
if (string.IsNullOrWhiteSpace(ImageFieldName))
{
return string.Empty;
}
return GetImageFieldHtml(item, ImageFieldName);
}
private static string GetImageFieldHtml(Item item, string imageFieldName)
{
Assert.ArgumentNotNull(item, "item");
Assert.ArgumentNotNullOrEmpty(imageFieldName, "imageFieldName");
Assert.ArgumentNotNullOrEmpty(RenderFieldPipelineName, "RenderFieldPipelineName");
if (item == null || item.Fields[imageFieldName] == null)
{
return string.Empty;
}
RenderFieldArgs args = new RenderFieldArgs { Item = item, FieldName = imageFieldName };
CorePipeline.Run(RenderFieldPipelineName, args);
if (args.Result.IsEmpty)
{
return string.Empty;
}
return args.Result.ToString();
}
protected virtual void AddImageHtmlToContent(SyndicationItem syndicationItem, string imageHtml)
{
if (string.IsNullOrWhiteSpace(imageHtml) || !(syndicationItem.Content is TextSyndicationContent))
{
return;
}
TextSyndicationContent content = syndicationItem.Content as TextSyndicationContent;
syndicationItem.Content = new TextSyndicationContent(string.Concat(imageHtml, content.Text), TextSyndicationContentKind.Html);
}
}
}
The class above ultimately overrides the RenderItem(Item item) method defined on Sitecore.Syndication.PublicFeed — it is declared virtual. The RenderItem(Item item) method above delegates to the RenderItem(Item item) method of Sitecore.Syndication.PublicFeed; grabs the System.ServiceModel.Syndication.SyndicationContent instance set in the Content property of the returned SyndicationItem object — this happens to be an instance of System.ServiceModel.Syndication.TextSyndicationContent; delegates to the <renderField> pipeline to generate HTML for the image set in the Image Field on the item; creates a new System.ServiceModel.Syndication.TextSyndicationContent instance with the HTML of the image combined with the HTML from the original TextSyndicationContent instance; sets the Content property with this new System.ServiceModel.Syndication.TextSyndicationContent instance; and returns the SyndicationItem instance to the caller.
Since I hate hard-coding things, I put the Image Field’s name and <renderField> pipeline’s name in a patch configuration file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<settings>
<setting name="RSS.Fields.ImageFieldName" value="Image" />
<setting name="Pipelines.RenderField" value="renderField" />
</settings>
</sitecore>
</configuration>
I then mapped the above subclass of Sitecore.Syndication.PublicFeed to the RSS Feed Item I created:
For testing, I added two Items — one with an image and another without an image:
After publishing everything, I loaded the RSS feed in my browser and saw the following:
If you know of other ways to add additional Item fields into Sitecore RSS feeds, please share in a comment.
Bundle CSS and JavaScript Files in Sitecore MVC
The other day I was poking around Sitecore.Forms.Mvc.dll — this assembly ships with Web Forms for Marketers (WFFM), and is used when WFFM is running on Sitecore MVC — and noticed WFFM does some bundling of JavaScript and CSS files:
WFFM uses the above class as an <initialize> pipeline processor. You can see this defined in Sitecore.Forms.Mvc.config:
This got me thinking: why not build my own class to serve as an <initialize> pipeline processor to bundle my CSS and JavaScript files?
As an experiment I whipped up the following class to do just that:
using System.Collections.Generic;
using System.Linq;
using System.Web.Optimization;
using Sitecore.Pipelines;
namespace Sitecore.Sandbox.Forms.Mvc.Pipelines
{
public class RegisterAdditionalFormBundles
{
public RegisterAdditionalFormBundles()
{
CssFiles = new List<string>();
JavaScriptFiles = new List<string>();
}
public void Process(PipelineArgs args)
{
BundleCollection bundles = GetBundleCollection();
if (bundles == null)
{
return;
}
AddBundle(bundles, CreateCssBundle());
AddBundle(bundles, CreateJavaScriptBundle());
}
protected virtual BundleCollection GetBundleCollection()
{
return BundleTable.Bundles;
}
protected virtual Bundle CreateCssBundle()
{
if (!CanBundleAssets(CssVirtualPath, CssFiles))
{
return null;
}
return new StyleBundle(CssVirtualPath).Include(CssFiles.ToArray());
}
protected virtual Bundle CreateJavaScriptBundle()
{
if (!CanBundleAssets(JavaScriptVirtualPath, JavaScriptFiles))
{
return null;
}
return new ScriptBundle(JavaScriptVirtualPath).Include(JavaScriptFiles.ToArray());
}
protected virtual bool CanBundleAssets(string virtualPath, IEnumerable<string> filePaths)
{
return !string.IsNullOrWhiteSpace(virtualPath)
&& filePaths != null
&& filePaths.Any();
}
private static void AddBundle(BundleCollection bundles, Bundle bundle)
{
if(bundle == null)
{
return;
}
bundles.Add(bundle);
}
private string CssVirtualPath { get; set; }
private List<string> CssFiles { get; set; }
private string JavaScriptVirtualPath { get; set; }
private List<string> JavaScriptFiles { get; set; }
}
}
The class above basically takes in a collection of CSS and JavaScript file paths as well as their virtual bundled paths — these are magically populated by Sitecore’s Configuration Factory using values provided by the configuration file shown below — iterates over both collections, and adds them to the BundleTable — the BundleTable is defined in System.Web.Optimization.dll.
I then glued everything together using a patch configuration file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<initialize>
<processor patch:after="processor[@type='Sitecore.Forms.Mvc.Pipelines.RegisterFormBundles, Sitecore.Forms.Mvc']"
type="Sitecore.Sandbox.Forms.Mvc.Pipelines.RegisterAdditionalFormBundles, Sitecore.Sandbox">
<CssVirtualPath>~/wffm-bundles/styles.css</CssVirtualPath>
<CssFiles hint="list">
<CssFile>~/css/uniform.aristo.css</CssFile>
</CssFiles>
<JavaScriptVirtualPath>~/wffm-bundles/scripts.js</JavaScriptVirtualPath>
<JavaScriptFiles hint="list">
<JavaScriptFile>~/js/jquery.min.js</JavaScriptFile>
<JavaScriptFile>~/js/jquery.uniform.min.js</JavaScriptFile>
<JavaScriptFile>~/js/bind.uniform.js</JavaScriptFile>
</JavaScriptFiles>
</processor>
</initialize>
</pipelines>
</sitecore>
</configuration>
I’m adding the <initialize> pipeline processor shown above after WFFM’s though theoretically you could add it anywhere within the <initialize> pipeline.
The CSS and JavaScript files defined in the configuration file above are from the Uniform project — this project includes CSS, JavaScript and images to make forms look nice, though I am in no way endorsing this project. I only needed some CSS and JavaScript files to spin up something quickly for testing.
For testing, I built the following View — it uses some helpers to render the <link> and <script> tags for the bundles — and tied it to my Layout in Sitecore:
@using System.Web.Optimization
@using Sitecore.Mvc
@using Sitecore.Mvc.Presentation
<!DOCTYPE html>
<html>
<head>
<title></title>
@Styles.Render("~/wffm-bundles/styles.css")
@Scripts.Render("~/wffm-bundles/scripts.js")
</head>
<body>
@Html.Sitecore().Placeholder("page content")
</body>
</html>
I then built a “Feedback” form in WFFM; mapped it to the “page content” placeholder defined in the View above; published it; and pulled it up in my browser. As you can see the code from the Uniform project styled the form:
For comparison, this is what the form looks like without the <initialize> pipeline processor above:
If you have any thoughts on this, or have alternative ways of bundling CSS and JavaScript files in your Sitecore MVC solutions, please share in a comment.
Turn Off the Attaching of Files in Emails Sent by Web Forms for Marketers in Sitecore
The other day Michael West pinged me to see if I could help someone in one of the SDN forums via this tweet:
The poster had asked if there were an easy way to disable the attachment of files on emails sent by the Web Forms for Marketers (WFFM) module in Sitecore.
After quickly peeking in the WFFM assemblies, I gave a potential solution — check out the thread to see what it was — though it did not work.
Tonight I looked further into how to accomplish this, and came up with the following solution:
using System;
using System.Net;
using Sitecore.Data;
using Sitecore.Form.Core.Client.Data.Submit;
using Sitecore.Form.Core.Pipelines.ProcessMessage;
using Sitecore.Pipelines;
namespace Sitecore.Sandbox.Form.Submit
{
public class SendMessage : Sitecore.Form.Submit.SendMessage
{
protected override void ExecuteMail(ID form, AdaptedResultList fields)
{
ProcessMessageArgs args = new ProcessMessageArgs(form, fields, this.MessageType);
base.To = base.To.TrimEnd(new char[] { ',', ';' });
base.LocalFrom = base.From.TrimEnd(new char[] { ',', ';' });
args.To.Append(base.To.Replace(";", ","));
args.From = base.From.Replace(";", ",");
// magically populated by WFFM via XML
args.IncludeAttachment = IncludeAttachments;
args.Mail.Append(base.Mail);
args.Subject.Append(base.Subject);
args.Recipient = this.Recipient;
args.RecipientGateway = this.RecipientGateway;
args.Host = base.Host;
args.Port = base.Port;
args.IsBodyHtml = this.IsBodyHtml;
args.EnableSsl = this.EnableSsl;
args.Data.Add("FromPhone", this.FromPhone ?? string.Empty);
string[] strArray = string.IsNullOrEmpty(base.Login) ? new string[] { string.Empty } : base.Login.Split(new char[] { '\\' });
if ((strArray.Length > 0) && !string.IsNullOrEmpty(strArray[0]))
{
if ((strArray.Length == 2) && !string.IsNullOrEmpty(strArray[1]))
{
args.Credentials = new NetworkCredential(strArray[1], base.Password, strArray[0]);
}
else
{
args.Credentials = new NetworkCredential(strArray[0], base.Password);
}
}
if (!string.IsNullOrEmpty(base.CC))
{
base.CC = base.CC.TrimEnd(new char[] { ',', ';' });
args.CC.Append(base.CC.Replace(";", ","));
}
if (!string.IsNullOrEmpty(base.BCC))
{
base.BCC = base.BCC.TrimEnd(new char[] { ',', ';' });
args.BCC.Append(base.BCC.Replace(";", ","));
}
CorePipeline.Run("processMessage", args);
}
// magically populated by WFFM via XML
private bool IncludeAttachments { get; set; }
}
}
The class above inherits from Sitecore.Form.Submit.SendMessage in Sitecore.Forms.Custom.dll, and overrides the ExecuteMail() method — this method is declared virtual in Sitecore.Form.Submit.SendMail which is the base class of Sitecore.Form.Submit.SendMessage.
Most of the code in ExecuteMail() above is from the ExecuteMail() method on Sitecore.Form.Submit.SendMessage, though with one minor difference: I have added a boolean property named “IncludeAttachments” which is magically populated by WFFM via an XML property declaration for this class:
I spun up a form quickly with some File Upload fields, and mapped the above Save Action to it:
I then navigated to my form, attached two images, and clicked the submit button:
As you can see, the images were not attached to the email:
I then went back to my Save Action; set the <IncludeAttachments> XML element’s value to be true; saved the form Item; published it; and resubmitted the form:
As you can see, the images were attached this time.
If you have any thoughts on this, please drop a comment.






















































