3

I am running on Sitecore 8.2 rev. 170728 with SXA 1.5

I have a requirement where we need to limit sub-items for an item. To implement it, I have created custom event handler that check the limit and if sub-item goes beyond that limit, it should show a warning message and delete current sub-item.

When i tested it, after getting warning message, I am getting following error in popup.

Server Error in '/' Application.
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[NullReferenceException: Object reference not set to an instance of an object.]
   Sitecore.XA.Foundation.Multisite.Services.PushCloneService.AddChild(Item item) +38
   System.EventHandler`1.Invoke(Object sender, TEventArgs e) +0
   Sitecore.Data.Engines.EngineCommand`2.RaiseEvent(EventHandler`1 handlers, Func`2 argsCreator) +128
   Sitecore.Data.Engines.DataCommands.CreateItemCommand.Executed() +20
   Sitecore.Data.Engines.EngineCommand`2.Execute() +165
   Sitecore.Data.Engines.DataEngine.CreateItem(String itemName, Item destination, ID templateId, ID newId, DateTime created) +47
   Sitecore.Data.Managers.ItemProvider.DoCreateAndReturnItem(String itemName, Item destination, ID templateId, ID newId, DateTime created) +81
   Sitecore.Data.Managers.ItemProvider.CreateItem(String itemName, Item destination, ID templateId, ID newId, DateTime created, SecurityCheck securityCheck) +381
   Sitecore.Data.Managers.ItemProvider.CreateItem(String itemName, Item destination, ID templateId, ID newId, SecurityCheck securityCheck) +53
   Sitecore.Data.Managers.DefaultItemManager.CreateItem(String itemName, Item destination, ID templateId, ID newId) +146
   Sitecore.Data.Managers.ItemManager.CreateItem(String itemName, Item destination, ID templateId, ID newId) +153
   Sitecore.Nexus.Data.DataCommands.AddFromTemplateCommand.(String , Item , ID , ID ) +9
   Sitecore.Data.Engines.DataCommands.AddFromTemplateCommand.DoExecute() +115
   Sitecore.Buckets.Commands.AddFromTemplateCommand.DoExecute() +1220
   Sitecore.Data.Engines.EngineCommand`2.Execute() +92
   Sitecore.Data.Managers.ItemProvider.AddFromTemplate(String itemName, ID templateId, Item destination, ID newId) +381
   Sitecore.Data.Managers.ItemManager.AddFromTemplate(String itemName, ID templateId, Item destination, ID newItemId) +153
   Sitecore.Data.Items.Item.Add(String name, TemplateID templateID, ID newItemID) +92
   Sitecore.Data.Items.Item.Add(String name, TemplateID templateID) +155
   Sitecore.Workflows.WorkflowContext.AddItem(String name, TemplateItem template, Item parent) +82
   Sitecore.Shell.Framework.Commands.AddMaster.Add(ClientPipelineArgs args) +983

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
   System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
   System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +210
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +146
   Sitecore.Reflection.ReflectionUtil.InvokeMethod(MethodInfo method, Object[] parameters, Object obj) +89
   Sitecore.Nexus.Pipelines.NexusPipelineApi.Resume(PipelineArgs args, Pipeline pipeline) +313
   Sitecore.Web.UI.Sheer.ClientPage.ResumePipeline() +224
   Sitecore.Web.UI.Sheer.ClientPage.OnPreRender(EventArgs e) +801
   Sitecore.Shell.Applications.ContentManager.ContentEditorPage.OnPreRender(EventArgs e) +24
   System.Web.UI.Control.PreRenderRecursiveInternal() +110
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3624

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.7.2558.0

Here is a code snippet:

public void OnItemCreated(object sender, EventArgs args)
    {
      var createdArgs = Event.ExtractParameter(args, 0) as ItemCreatedEventArgs;

      Sitecore.Diagnostics.Assert.IsNotNull(createdArgs, "args");
      if (createdArgs != null)
      {
        Sitecore.Diagnostics.Assert.IsNotNull(createdArgs.Item, "item");
        if (createdArgs.Item != null)
        {
          var item = createdArgs.Item;


          if (item.Parent != null &&
              item.Parent.TemplateName == "templatename" &&
              item.Parent.Children.Count() > 6)
          {
            // Delete the item, warn user
            var itemName = item.Parent.Name;

            SheerResponse.Alert(
                String.Format("Sorry, you cannot add more than 6 items to {0}.",
                                  itemName), new string[0]);
            using (new Sitecore.SecurityModel.SecurityDisabler())
            {
              if (item != null)
              {
                item.Editing.BeginEdit();
                item.Delete();
                item.Editing.EndEdit();
              }
            }
            //Sitecore.Shell.Framework.Items.Delete(new Sitecore.Data.Items.Item[] { item}, string.Empty);
            return;
          }
        }
      }
    }

Any help would be much appreciated.

  • Try adding createdArgs .AbortPipeline(); after deleting the item – Marek Musielak Jan 7 '18 at 12:58
  • @MarekMusielak : Thanks for the response. But, I am using event handler. Not pipeline. So i cannot use abortpipeline() method. – user3024615 Jan 7 '18 at 13:05
  • 1
    Sorry, my bad. Have you tried using "item:creating" or "item:adding" instead of "item:created"? And use ((SitecoreEventArgs)args).Result.Cancel = true; – Marek Musielak Jan 7 '18 at 13:06
  • 1
    You do not need to wrap item.Delete() around BeginEdits and EndEdits. – Pete Navarra Jan 7 '18 at 16:05
  • @MarekMusielak: Thanks Marek it's working as expected.But, I have used item:creating event handler. This will invoke while item create using UI. But this event not called while create copying, duplicating and via api. How to achieve the same while copy,duplicate. – user3024615 Jan 7 '18 at 18:18
3

Building logic into the custom item events themselves makes the functionality rigid and not easily changed.

A quick google search on limiting no of child sitecore items through rules has yielded good results.

'FES Sitecore' has written a fantastic blog about this. Restricting the number of sub items in Sitecore. Hope this is what you are looking for. Below summary is condensed version of above blog post. Strongly recommend to visit the link for deep dive.

At the core, you should try using the Sitecore Rules. This rule can run when an item is created or moved from some other location in content tree

  1. Create a Custom Condition Rule like AnyChildItemCountCondition derived from BaseOperatorCondition
  2. Or if you would like to restrict by template type you can create something like TemplateChildItemCountCondition
  3. Configure the Custom Rule Condition in /sitecore/system/Settings/Rules/Definitions/Elements/Item Hierarchy. Rule condition might look like where the number of [templateid,Tree,root=/sitecore/templates,specific template] child items [operatorid,Operator,,compares to] [count,Integer,,number]
  4. Then create custom actions like DeleteItemAction or CancelItemMoveAction
  5. We need to configure these in a similar way to how we configured the conditions. We'll create a new group for these rules so create a new "Element Folder" like "My Item Actions" under /sitecore/system/Settings/Rules/Definitions/Elements
  6. Define a custom Tag for the above custom actions. Tags are used to define which groups of conditions or actions are shown under what circumstances. Something like 'My Item Actions' under /sitecore/system/Settings/Rules/Definitions/Tags
  7. Associate that tag with our custom action group so edit this item /sitecore/system/Settings/Rules/Definitions/Elements/My Item Actions/Tags/Default
  8. Define custom rules context folder. Something similar to /sitecore/system/Settings/Rules/Item Saved and create Rules for Item creation and Item Move. Rule might appear under /sitecore/system/Settings/Rules/My Custom Items/Rules
  9. Now the fun part of actually running the rules from the custom event handlers

    <sitecore> <events> <event name="item:moved"> <handler type="MyNamespace.RunRules, MyAssembly" method="OnMoved"/> </event> <event name="item:created"> <handler type="MyNamespace.RunRules, MyAssembly" method="OnCreated"/> </event> </events> </sitecore>

Sample class

public class RunRules
{
    public void OnCreated(object sender, EventArgs args)
    {
        var itemArgs = Event.ExtractParameter(args, 0) as ItemCreatedEventArgs;

        if (itemArgs == null || itemArgs.Item == null)
        {
            return;
        }

        Execute(itemArgs.Item, "/sitecore/system/Settings/Rules/My Created Events/Rules/*[@@templatename='Rule']");
    }

    public void OnMoved(object sender, EventArgs args)
    {
        Item item = Event.ExtractParameter(args, 0) as Item;
        ID oldParentID = Event.ExtractParameter(args, 1) as ID;

        if (item == null)
        {
            return;
        }

        Execute(item, "/sitecore/system/Settings/Rules/My Moved Events/Rules/*[@@templatename='Rule']",
            new NameValueCollection { { "OldParentID", oldParentID.ToString() } });
    }

    private void Execute(Item item, string query, NameValueCollection parameters = null)
    {
        Item[] ruleItems = item.Database.SelectItems(query);

        if (ruleItems.Length == 0)
        {
            return;
        }

        var ruleContext = new RuleContext();

        ruleContext.Item = item;

        if (parameters != null)
        {
            foreach (string key in parameters.AllKeys)
            {
                ruleContext.Parameters.Add(key, parameters[key]);
            }
        }

        foreach (var ruleItem in ruleItems)
        {
            var rules = RuleFactory.ParseRules<RuleContext>(ruleItem.Database, ruleItem["Rule"]);

            if (rules != null && rules.Count > 0)
            {
                rules.Run(ruleContext);
            }
        }
    }
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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