LibrarySites.Banner

Sitecore Item Buckets with meaningful folder paths

Organizing items using buckets is one of the most useful features of Sitecore 7.  The ability to quickly bucket and un-bucket sections makes it an even more attractive feature. 

One drawback however, is that out of the box it only creates folder paths based on an item’s creation date.  This isn’t helpful in scenarios where the item’s creation date is different from the content date, such as news articles created in advance for future publication.

To solve this problem, I decided to extend the bucket folder creation method and add configuration that lets you specify a date field for every template.

Here’s how to do this -

Step 1: First, create a bucket configuration and add it to a Settings file

  

<!-- extension to sitecore item bucket folder path creation method

                                 add template and date field mappings to use

                                 if a template is not mapped item's creation date will be used

    -->

    <xcoreBucketConfiguration>

      <dateFieldMappings>

        <mapping template="User Defined/XCentium/Modules/News Item" field="News Date"></mapping>

                                <mapping template="User Defined/XCentium/Modules/Event Item" field="Event Date"></mapping>

                                <mapping template="User Defined/XCentium/Modules/Blog Date" field="Publishing Date"></mapping>

      </dateFieldMappings>

    </xcoreBucketConfiguration>

 

This allows you to specify a date field you want to use for a template.  You can now add a different field for each template.

Step 2: Next, we need some code to read this configuration.

 

 

private static SafeDictionary<ID, string> _templateDateFieldCollection;

 

        private static SafeDictionary<ID, string> TemplateDateFieldCollection

        {

            get

            {

                return _templateDateFieldCollection;

            }

            set

            {

                Assert.ArgumentNotNull(value, "value");

                _templateDateFieldCollection = value;

            }

        }

 

 

        static BucketFolderConfigurationManager()

        {

            Initialize();

        }

        private static void Initialize()

        {

            TemplateDateFieldCollection = new SafeDictionary<ID, string>();

            Database masterDB = Sitecore.Context.ContentDatabase ?? Sitecore.Configuration.Factory.GetDatabase("master");

            Assert.IsNotNull(masterDB, "content database is not defined");

            foreach (XmlNode node in Factory.GetConfigNodes("xcoreBucketConfiguration/dateFieldMappings/mapping"))

            {

 

                string templateName = XmlUtil.GetAttribute("template", node);

                string fieldName = XmlUtil.GetAttribute("field", node);

                TemplateItem template = masterDB.Templates[templateName];

                if (template != null && !string.IsNullOrEmpty(fieldName))

                {

                    TemplateDateFieldCollection.Add(template.ID, fieldName);

                }

                else

                {

                    Log.Info(string.Format("Could not find template : {0}", templateName), new object());

                }

 

            }

        }

 

        public static string GetDateFieldName(TemplateItem template)

        {

            return TemplateDateFieldCollection.ContainsKey(template.ID) ? TemplateDateFieldCollection[template.ID] : null;

        }

    }

<setting name="BucketConfiguration.DynamicBucketFolderPath" >

        <patch:attribute name="value">XCore.SitecoreExtentions.Buckets.DateBasedFolderPath,XCore.SitecoreExtentions</patch:attribute>

      </setting>


Step 3
: Next, create DateBasedFolderPath that implements IDynamicBucketFolderPath and update BucketConfiguration.DynamicBucketFolderPath setting to use this new class.

 

    public class DateBasedFolderPath : IDynamicBucketFolderPath

    {

        private Database _contentDatabase;

        protected Database ContentDatabase

        {

            get

            {

                if (_contentDatabase == null)

                {

                    _contentDatabase = Sitecore.Context.ContentDatabase ?? Sitecore.Configuration.Factory.GetDatabase("master");

                }

                return _contentDatabase;

            }

        }

        public string GetFolderPath(ID newItemId, ID parentItemId, DateTime creationDateOfNewItem)

        {

            Item newItem = this.ContentDatabase != null ? this.ContentDatabase.GetItem(newItemId) : null;

            if (newItem != null)

            {

                string fieldName = BucketFolderConfigurationManager.GetDateFieldName(newItem.Template);

                if (!string.IsNullOrEmpty(fieldName))

                {

                    DateTime creationDateTime = EnsureAndGetDate(newItem, fieldName, creationDateOfNewItem);

                    return creationDateTime.ToString(BucketConfigurationSettings.BucketFolderPath, Context.Culture);

                }

            }

 

            return creationDateOfNewItem.ToString(BucketConfigurationSettings.BucketFolderPath, Context.Culture);

 

        }

 

        protected DateTime EnsureAndGetDate(Item item, string dateFieldName, DateTime defaultDateTime)

        {

            DateField dateField = item.Fields[dateFieldName];

            if (dateField == null)

                return defaultDateTime;

            DateTime result = dateField.DateTime;

            if (!string.IsNullOrEmpty(dateField.InnerField.Value))

            {

                return result;

            }

            using (new EditContext(item))

            {

                dateField.Value = DateUtil.ToIsoDate(defaultDateTime);

                return DateUtil.IsoDateToDateTime(dateField.InnerField.Value);

            }

        }

    }

 

 

A couple of things to note here are

  1. GetFolderPath method uses the custom BucketFolderConfigurationManager class created in Step 2 that reads the field names for templates from the configuration.
  2. If the field name is not specified or if the item does not have that date field it defaults to creation date of the new item.


Step 4
: The code above will take care of correctly organizing the items when bucket-sync command is executed.  However, this still does not solve the problem of moving items to the correct folder based on item creation dates because when the item is first created it does not have the date field filled in.  To solve this, we’ll add a new item saved event handler.

 

<events>

      <event name="item:saved">

        <handler type="XCore.SitecoreExtentions.Buckets.EventHandlers.ItemSaved, XCore.SitecoreExtentions" method="Process"/>

      </event>

    </events>

 

public void Process(object sender, EventArgs args)

        {

            Assert.ArgumentNotNull(sender, "sender");

            Assert.ArgumentNotNull(args, "args");

 

            Item savedItem = Event.ExtractParameter(args, 0) as Item;

            Assert.IsNotNull(savedItem, "saved item can not be null");

            if (savedItem == null)

            {

                Log.Error("error creating item", this);

                return;

            }

            if (!savedItem.Database.Name.Equals("master", StringComparison.InvariantCultureIgnoreCase))

                return;

            if (!BucketManager.IsItemContainedWithinBucket(savedItem))

            {

                Log.Debug("Item {0}  is not contained in a bucket", savedItem);

                return;

            }

            Item bucketItem = savedItem.GetParentBucketItemOrParent();

            if (!BucketManager.IsBucket(bucketItem))

                return;

 

            BucketManager.MoveItemIntoBucket(savedItem, bucketItem);

 

        }

 

Hope this helps you as much as it helped me.

Download the source code