2

I have a custom bucket that organizes items using a custom rule based on the value of a date field called "Event Start Date" on each page item.

Bucket structure

I'm trying to give content authors the ability to manually sync the bucket because items do not bucket automatically due to the "Event Start Date" field being empty when a new page is created.

This is what I can see as a super admin:

enter image description here

I have made our author role a member of sitecore\Sitecore Client Bucket Management, but all the author can see is:

enter image description here

It still doesn't work even when I grant specific permissions on the item in question (this is the security viewer):

enter image description here

As a side note, I'm not finding much information on this anywhere. Is the standard practice to auto sync buckets on save or item add events?

6
  • The sync should be automatic and transparent to the author. I would not think that they have any need to do this activity. Jan 1, 2020 at 22:29
  • @MichaelWest Automatic bucket syncing? I've never seen that before -- not without custom code. Please advise. Jan 2, 2020 at 0:56
  • sitecorespark.com/article/buckets my understanding of buckets is that it's a one-time setup. As items are added to the tree Sitecore handles the structure based on your bucketing rule. Sync is only needed when a configuration change occurs. Jan 2, 2020 at 3:34
  • When is bucket syncing not happening for you? As @MichaelWest says, the items should automatically be created in the right spot automatically. Jan 2, 2020 at 12:12
  • @DanSinclair I see what you mean. I have edited the question to provide more information. The reason why it's not bucketing automatically is because I have a custom bucketing rule based on a date field on the page items themselves (in this case it's called Event Start Date). Jan 2, 2020 at 12:34

2 Answers 2

1

Reviewing the code for this ribbon button (Sitecore.Buckets.Commands.SyncBucket in the Sitecore.Buckets.dll) reveals that the user must be an Administrator to have access to this button:

public override CommandState QueryState(CommandContext context)
{
    Error.AssertObject(context, "context");
    Item items = context.Items[0];
    BucketSecurityManager bucketSecurityManager = new BucketSecurityManager(items);
    if (items.Appearance.ReadOnly)
    {
        return CommandState.Disabled;
    }
    if (!items.Locking.HasLock() && !Context.User.IsAdministrator)
    {
        return CommandState.Disabled;
    }
    if (items.Locking.IsLocked() && !items.Locking.HasLock())
    {
        return CommandState.Disabled;
    }
    if (!bucketSecurityManager.IsAllowedToCreateBucket)
    {
        return CommandState.Disabled;
    }
    if (!items.IsBucketItemCheck())
    {
        return CommandState.Disabled;
    }
    return CommandState.Enabled;
}

Next steps

You could theoretically override this behavior by creating a subclass of SyncBucket and overriding the QueryState method to evaluate your own security rules, then add a patch config to update the class used for the item:syncbucket command to use your custom class instead.

Note: a better solution, however, is to automatically sync the items as they are updated (using something like this solution), instead of requiring authors to manually click the Sync button.

For the curious: How to find the above information?

I reviewed the security for the Sync button in the ribbon by following the below steps:

  1. Find the ribbon button in the core database (/sitecore/content/Applications/Content Editor/Ribbons/Chunks/Item Buckets/Sync)
  2. Check its security and the security of its parent
    • Its parent's security gives access to users of roles sitecore\Sitecore Client Configuring and sitecore\Sitecore Client Bucket Management

Even after granting a user access to one of those roles, however, does not allow them to click the Sync button, however, so I reviewed the code:

  1. The ribbon button item in the core database has the command name item:syncbucket
  2. Looking at /sitecore/admin/showconfig.aspx, I found that it points to Sitecore.Buckets.Commands.SyncBucket in the Sitecore.Buckets.dll
  3. In that class, I found the above QueryState method, which shows how it determines whether to enable or disable the button.
2

You can achieve this by programmatically moving the bucketed item to the root of the bucket with the BucketManager. Doing this will force it to reevaluate the bucket rules and reorganize it:

BucketManager.MoveItemIntoBucket(bucketedItem, bucketItem);

Note that this is different from BucketManager.Sync(bucketItem) because it does not sync the whole bucket, but instead handles just the single item that was changed.

In our solutions, we typically create an item:saved event handler to do this automatically:

using Sitecore.Buckets.Managers;
using Sitecore.Buckets.Util;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Events;
using System;
using System.Text.RegularExpressions;

namespace Custom.Events.ItemSaved
{
    public class ReorganizeBucketedItemInBucket
    {
        public void OnItemSaved(object sender, EventArgs args)
        {
            var bucketedItem = Event.ExtractParameter(args, 0) as Item;

            // If we don't have an item or we're not saving in the master DB, ignore this save
            if (bucketedItem == null || !"master".Equals(bucketedItem.Database?.Name, StringComparison.OrdinalIgnoreCase))
                return;

            if (!bucketedItem.TemplateID.Equals(new ID("{bucketed-item-template-id}"))) return;

            var itemChanges = Event.ExtractParameter(args, 1) as ItemChanges;

            // If there were no changes or the changes didn't include the date field, ignore this save
            if (itemChanges == null || !itemChanges.HasFieldsChanged || !itemChanges.IsFieldModified(new ID("{field-id-of-date-field}")))
                return;

            Item bucketItem = bucketedItem.Axes.SelectSingleItem($"{EscapePath(bucketedItem.Paths.FullPath)}/ancestor-or-self::*[@@templateid = '{{bucket-container-template-id}}']");

            // If this item isn't in a bucket (or is in a bucket of another, unexpected type), ignore it
            if (bucketItem == null) return;

            Item parent = bucketedItem.Parent;
            BucketManager.MoveItemIntoBucket(bucketedItem, bucketItem);

            // Delete empty ancestor bucket folders
            while (parent != null && !parent.HasChildren && parent.TemplateID == BucketConfigurationSettings.BucketTemplateId)
            {
                Item tempParent = parent.Parent;
                parent.Delete();
                parent = tempParent;
            }
        }

        /// <summary>
        /// Wraps each segment of a sitecore path with "#"'s
        /// </summary>
        public string EscapePath(string path)
        {
            return Regex.Replace(path, @"([^/]+)", "#$1#").Replace("#*#", "*");
        }
    }
}

And don't forget your patch config, of course:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <events>
            <event name="item:saved">
                <handler type="Custom.Events.ItemSaved.ReorganizeBucketedItemInBucket, Custom.Events" method="OnItemSaved"></handler>
            </event>
        </events>
    </sitecore>
</configuration>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.