Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Martin Griffiths 792 posts 1190 karma points c-trib
    Mar 01, 2021 @ 11:49
    Martin Griffiths
    0

    Event execution halted with a cancel operation

    Hey all

    So i've hit an unusual (probably a bit edge case for most) situation that could be considered a bug. If anyone has encountered this, it would be great to get some feedback.

    I'm using the rather awesome (and should be core) Nexu plugin, to check for content and media usage throughout our site. In Umbraco 7 i've not found any mechanism to prevent media deletion (only warns), I think this function exists in Nexu for Umbraco 8, so until then, i've written some code to prevent deletes when content editors attempt to remove media in-use!

    The code taps into the Umbraco Media Service and the Trashing event. It simply checks for Nexu relations and also does a little recursion on the selected tree fragment, firing a CancelOperation event if any (cumulative) links are counted.

    This is where it gets a little hairy on expected behaviour, there are essentially two main ways you can delete media. One way is via the tree and the other is via a multi-select action in the right pane/window.

    I've noticed that within one single UI event the CancelOperation event seems to stop any further execution irrespective of how many times the MediaService Trashing event should be raised.

    In the tree delete scenario this isn't a major concern, as due to nesting you get the impression (as an end user) that the function should complete as a whole to be successful. It's arguable that it would be nice for the backoffice to display a message that simply says, only unlinked items were successfully deleted. But it's not something that should leave a bad taste in the mouth!

    In the second deletion method, you multi-select three media items and the first two don't have any links, as the execution passes over each one they're recycled until it hits the third item and raises a CancelOperation event. You get the expected error message for the final item and the first two are deleted. All good! Now, if you select the same three items in reverse order the execution hits the first item with links, halts the execution, fires the error message and the next two are completely ignored! Not so good! Bad taste in the mouth! Imagine if you'd multi-selected a significant number of images, only for it to halt half way through the process and what error do you display? "Only some of the images were deleted!" PAH!

    It's not an absolutely terrible scenario, as I can now ensure our content isn't trashed when it's in-use. But from a user perspective it's a bit janky.

    Any thoughts on this appreciated.

    Code:

    public class MediaTrashingEvents : ApplicationEventHandler
    {
        protected override void ApplicationStarted(UmbracoApplicationBase app, ApplicationContext ctx)
        {
            MediaService.Trashing += MediaService_Trashing;
        }
    
        private void MediaService_Trashing(IMediaService sender, MoveEventArgs<IMedia> e)
        {
            int mediaWithLinksCount = 0;
    
            foreach (MoveEventInfo<IMedia> moveEventInfo in e.MoveInfoCollection)
            {
                IRelationService rs = ApplicationContext.Current.Services.RelationService;
                IEnumerable<IRelation> parentRelations = rs.GetByChildId(moveEventInfo.Entity.Id);
    
                if (parentRelations.Any())
                    mediaWithLinksCount += 1;
    
                if (moveEventInfo.Entity.Children().Any())
                    mediaWithLinksCount = processChildren(moveEventInfo.Entity.Children(), mediaWithLinksCount);
            }
    
            if (mediaWithLinksCount > 0)
                e.CancelOperation(new EventMessage("Warning!", "You are trying to delete media items that are linked to content, items with links cannot be deleted", EventMessageType.Error));
        }
    
        private int processChildren(IEnumerable<IMedia> media, int count)
        {
            foreach (IMedia item in media)
            {
                IRelationService rs = ApplicationContext.Current.Services.RelationService;
                IEnumerable<IRelation> parentRelations = rs.GetByChildId(item.Id);
    
                if (parentRelations.Any())
                    count++;
    
                if (item.Children().Any())
                    count = processChildren(item.Children(), count); // recursive call here
            }
            return count;
        }
    }
    
Please Sign in or register to post replies

Write your reply to:

Draft