Block deletion of nodes

Aug 6, 2008 at 12:31 PM
Following the discussion in this thread: 29305 I would like request an option to prevent deletion of an item.
My use for this option is that in some webapplications there is a fixed route in pages to follow and the path may not be broken by deletion of a page.
For instance in a webshop, the user moust follow some pages to order an article. All pages are pre-declared in N2 when deploying the webshop...if a user would delete one of those pages the webshop would not function anymore.

The setting does not have to be influenced by a user, in code would be enough (for me).
It this perhaps already possible?
Coordinator
Aug 6, 2008 at 11:28 PM
Does it work to subscribe to the deleting event?
Aug 7, 2008 at 5:23 PM
I could not really get it to work...can you assist me?
I tried with the code below, but IAutoStart.Start() is not hit in the execution when deleting the item...what am I doing wrong?

using N2;
using N2.Details;
using N2.Persistence;
using N2.Plugin;

[Definition("Delete test", "deltest")]
[WithEditableTitle("Paginatitel", 10)]
[WithEditableName("Paginanaam en -url", 20)]
public class deltest : PageItem, IAutoStart
{
    private readonly IPersister persister;
    public deltest(IPersister persister)
    {
        this.persister = persister;
    }
    private void ItemDeletingEvenHandler(object sender, CancellableItemEventArgs e)
    {
        OnItemDeleting(e.AffectedItem);
    }
    protected virtual void OnItemDeleting(ContentItem item)
    {
        N2Exception ex = new N2Exception("Item deletion is not allowed");
        throw ex;
    }
    public virtual void Start()
    {
        persister.ItemDeleting += ItemDeletingEvenHandler;
    }
    public virtual void Stop()
    {
        persister.ItemDeleting -= ItemDeletingEvenHandler;
    }
    void IAutoStart.Start()
    {
        Start();
    }
}
Coordinator
Aug 7, 2008 at 10:51 PM
I realise this is tricker than it need to be. You could either subscribe to the events from within your code, e.g. init event in global asax.

I generally follow this pattern:

[AutoInitialize]
public class MyInitializer : IPluginInitializer
{
        public void Initialize(IEngine engine)
        {
            engine.AddComponent("mystuff", typeof(MyDeleteHandler));
        }
}
public class MyDeleteHandler : IAutoStart
{
  public MyDeleteHandler(IPersister persister)
  {
    //save persister reference
  }
  public void Start()
  {
    //subscribe to events
  }
}


Aug 7, 2008 at 11:40 PM
Edited Aug 8, 2008 at 12:04 AM
If I use the code below in a file hooks.cs it looks like it is working, the exception is thrown and the item is still in the tree, but after a refresh it is moved to the trash...
Is throwing an exception not enough to prevent the deletion?

using N2;
using N2.Engine;
using N2.Persistence;
using N2.Plugin;

[AutoInitialize]
public class AddCustomHooks : IPluginInitializer
{
    public void Initialize(IEngine engine)
    {
        engine.AddComponent("CustomHooks", typeof(CustomHooks));
    }
}
public class CustomHooks : IAutoStart
{
    private readonly IPersister persister;
    public CustomHooks(IPersister persister)
    {
        this.persister = persister;
    }

    private void ItemDeletingEvenHandler(object sender, CancellableItemEventArgs e)
    {
        OnItemDeleting(e.AffectedItem, e);
    }
    protected virtual void OnItemDeleting(ContentItem item, CancellableItemEventArgs e)
    {
        //custom handling which item can and which cannot be deleted
        N2Exception ex = new N2Exception("Deletion of this item is not allowed");
        e.Cancel = true;
        throw ex;
    }
    public virtual void Start()
    {
        persister.ItemDeleting += ItemDeletingEvenHandler;
    }
    public virtual void Stop()
    {
        persister.ItemDeleting -= ItemDeletingEvenHandler;
    }
}


Coordinator
Aug 8, 2008 at 7:30 AM
Your event handler probably kicks in after the trash handler's. Try intercepting moving as well.
Aug 8, 2008 at 9:29 AM
With the code below, no success...
On deletion the ItemSavingEvenHandler is hit first, after that the ItemDeletingEvenHandler and that's it...
When I try to move an item ItemMovingEvenHandler is hit and the move is indeed blocked, but the handler is not hit when deleting...


using N2;
using N2.Engine;
using N2.Persistence;
using N2.Plugin;

[AutoInitialize]
public class AddCustomHooks : IPluginInitializer
{
    public void Initialize(IEngine engine)
    {
        engine.AddComponent("CustomHooks", typeof(CustomHooks));
    }
}
public class CustomHooks : IAutoStart
{
    private readonly IPersister persister;
    public CustomHooks(IPersister persister)
    {
        this.persister = persister;
    }

    #region Event Dispatchers
    private void ItemSavingEvenHandler(object sender, CancellableItemEventArgs e)
    {
        OnItemSaving(e.AffectedItem, e);
    }

    private void ItemMovingEvenHandler(object sender, CancellableDestinationEventArgs e)
    {
        OnItemMoving(e.AffectedItem, e.Destination, e);
    }
    private void ItemCopyingEvenHandler(object sender, CancellableDestinationEventArgs e)
    {
        OnItemCopying(e.AffectedItem, e.Destination, e);
    }
    private void ItemDeletingEvenHandler(object sender, CancellableItemEventArgs e)
    {
        OnItemDeleting(e.AffectedItem, e);
    }
    #endregion


    #region Event Handlers
    protected virtual void OnItemSaving(ContentItem item, CancellableItemEventArgs e)
    {
        //N2Exception ex = new N2Exception("Savingof this item is not allowed");
        //e.Cancel = true;
        //throw ex;
    }
    protected virtual void OnItemMoving(ContentItem source, ContentItem destination, CancellableDestinationEventArgs e)
    {
        N2Exception ex = new N2Exception("Moving of this item is not allowed");
        e.Cancel = true;
        throw ex;
    }
    protected virtual void OnItemCopying(ContentItem source, ContentItem destination, CancellableDestinationEventArgs e)
    {
        //N2Exception ex = new N2Exception("Copying of this item is not allowed");
        //e.Cancel = true;
        //throw ex;
    }
    protected virtual void OnItemDeleting(ContentItem item, CancellableItemEventArgs e)
    {
        //custom handling which item can and which cannot be deleted
        N2Exception ex = new N2Exception("Deletion of this item is not allowed");
        e.Cancel = true;
        throw ex;
    }
    #endregion

    #region IStartable Members
    public virtual void Start()
    {
        persister.ItemCopying += ItemCopyingEvenHandler;
        persister.ItemDeleting += ItemDeletingEvenHandler;
        persister.ItemMoving += ItemMovingEvenHandler;
        persister.ItemSaving += ItemSavingEvenHandler;
    }
    public virtual void Stop()
    {
        persister.ItemCopying -= ItemCopyingEvenHandler;
        persister.ItemDeleting -= ItemDeletingEvenHandler;
        persister.ItemMoving -= ItemMovingEvenHandler;
        persister.ItemSaving -= ItemSavingEvenHandler;
    }
    #endregion
}
Coordinator
Aug 8, 2008 at 10:54 AM
Okay, looking at the code I noticed the throwing is made like this:

item.AddTo(TrashContainer);
persister.Save(item);


This means you can either override ContentItem.AddTo or subscribe to Save and check the content item's Parent
Aug 8, 2008 at 11:15 PM
Well, it works, cancelling a deletion by canceling the save of it :-)
A bit of a strange workaround, but working.

Thanks!
Are you planning (or thinking of) adding more support for hooks like I mentioned (insert/update/delete) ?
Coordinator
Aug 9, 2008 at 5:18 PM
Okay, I added an event to the trash handler that could help you:

N2.Context.Current.Resolve<ITrashHandler>.ItemThrowing += ...

Maybe an interface that can be applied to the content item would be useful? The framework could check for the presence of that interface and call methods on it.