Home » Configuration » Augment the Sitecore UploadWatcher to Delete Files from the Upload Directory After Uploading to the Media Library

Augment the Sitecore UploadWatcher to Delete Files from the Upload Directory After Uploading to the Media Library

Sitecore Technology MVP 2016
Sitecore MVP 2015
Sitecore MVP 2014

Enter your email address to follow this blog and receive notifications of new posts by email.

In my previous post I discussed the Sitecore UploadWatcher — a Sitecore.IO.FileWatcher which monitors the /upload directory of your Sitecore instance and uploads files dropped into that directory into the Media Library.

One thing I could never find in the UploadWatcher is functionality to delete files in the /upload directory after they are uploaded into the Media Library — if you know of an “out of the box” way of doing this, please drop a comment.

After peeking into Sitecore.Resources.Media.UploadWatcher using .NET Reflector, I discovered I could add this functionality quite easily since its Created() method — this method handles the uploading of the files into the Media Library by delegating to Sitecore.Resources.Media.MediaManager.Creator.FileCreated() — is overridable. In theory, all I would need to do would be to subclass the UploadWatcher class; override the Created() method; delegate to the base class’ Created() method to handle the upload of the file to the Media Library; then delete the file once the base class’ Create() method was done executing.

After coding, experimenting and refactoring, I built the following class that subclasses Sitecore.Resources.Media.UploadWatcher:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;

using Sitecore.Configuration;
using Sitecore.Diagnostics;
using Sitecore.IO;
using Sitecore.Resources.Media;
using Sitecore.Xml;

namespace Sitecore.Sandbox.Resources.Media
{
    public class CleanupUploadWatcher : UploadWatcher
    {
        private IEnumerable<string> PathsToIgnore { get; set; }

        private IEnumerable<string> FileNamePartsToIgnore { get; set; }

        private bool ShouldDeleteAfterUpload { get; set; }

        public CleanupUploadWatcher()
            : base()
        {
            Initialize();
        }

        private void Initialize()
        {
            IEnumerable<XmlNode> configNodes = GetIgnoreConfigNodes();
            PathsToIgnore = GetPathsToIgnore(configNodes);
            FileNamePartsToIgnore = GetFileNamePartsToIgnore(configNodes);
            ShouldDeleteAfterUpload = GetShouldDeleteAfterUpload();
        }

        protected virtual IEnumerable<XmlNode> GetIgnoreConfigNodes()
        {
            string configNodeRoot = GetConfigNodeRoot();
            if (string.IsNullOrWhiteSpace(configNodeRoot))
            {
                return Enumerable.Empty<XmlNode>();
            }

            XmlNode rootNode = Factory.GetConfigNode(configNodeRoot);
            if (rootNode == null)
            {
                return Enumerable.Empty<XmlNode>();
            }

            return XmlUtil.GetChildNodes(rootNode, true);
        }

        protected virtual string GetConfigNodeRoot()
        {
            return "mediaLibrary/watcher/ignoreList";
        }

        protected virtual IEnumerable<string> GetPathsToIgnore(IEnumerable<XmlNode> nodes)
        {
            if (IsEmpty(nodes))
            {
                return Enumerable.Empty<string>();
            }

            HashSet<string> pathsToIgnore = new HashSet<string>();
            foreach (XmlNode node in nodes)
            {
                string containsValue = XmlUtil.GetAttribute("contains", node);
                if (ShouldAddContainsValue(containsValue, node, "ignorepath"))
                {
                    pathsToIgnore.Add(containsValue);
                }
            }

            return pathsToIgnore;
        }

        protected virtual IEnumerable<string> GetFileNamePartsToIgnore(IEnumerable<XmlNode> nodes)
        {
            if (IsEmpty(nodes))
            {
                return Enumerable.Empty<string>();
            }

            HashSet<string> partsToIgnore = new HashSet<string>();
            foreach (XmlNode node in nodes)
            {
                string containsValue = XmlUtil.GetAttribute("contains", node);
                if (ShouldAddContainsValue(containsValue, node, "ignore"))
                {
                    partsToIgnore.Add(containsValue);
                }
            }

            return partsToIgnore;
        }

        protected virtual bool ShouldAddContainsValue(string containsValue, XmlNode node, string targetNodeName)
        {
            return !string.IsNullOrWhiteSpace(containsValue)
                    && node != null
                    && string.Equals(node.Name, targetNodeName, StringComparison.OrdinalIgnoreCase);
        }

        protected static bool IsEmpty<T>(IEnumerable<T> collection)
        {
            return collection == null || !collection.Any();
        }

        protected override void Created(string filePath)
        {
            Assert.ArgumentNotNullOrEmpty(filePath, "filePath");
            if (!ShouldIgnoreFile(filePath))
            {
                base.Created(filePath);
                DeleteFile(filePath);
            }
        }

        protected virtual bool GetShouldDeleteAfterUpload()
        {
            XmlNode node = Factory.GetConfigNode("watchers/media/additionalSettings");
            if (node == null)
            {
                return false;
            }

            string deleteAfterUploadValue = XmlUtil.GetAttribute("deleteAfterUpload", node);
            if(string.IsNullOrWhiteSpace(deleteAfterUploadValue))
            {
                return false;
            }
            
            bool shouldDelete;
            bool.TryParse(deleteAfterUploadValue, out shouldDelete);
            return shouldDelete;
        }

        protected virtual bool ShouldIgnoreFile(string filePath)
        {
            Assert.ArgumentNotNullOrEmpty(filePath, "filePath");
            foreach (string path in PathsToIgnore)
            {
                if (ContainsSubstring(filePath, path))
                {
                    return true;
                }
            }

            string fileName = Path.GetFileName(filePath);
            foreach (string part in FileNamePartsToIgnore)
            {
                if(ContainsSubstring(fileName, part))
                {
                    return true;
                }
            }

            return false;
        }

        protected virtual bool ContainsSubstring(string value, string substring)
        {
            if(string.IsNullOrWhiteSpace(value) || string.IsNullOrWhiteSpace(substring))
            {
                return false;
            }

            return value.IndexOf(substring, StringComparison.OrdinalIgnoreCase) > -1;
        }

        protected virtual void DeleteFile(string filePath)
        {
            Assert.ArgumentNotNullOrEmpty(filePath, "filePath");
            if(!ShouldDeleteAfterUpload)
            {
                return;
            }

            try
            {
                FileUtil.Delete(filePath);
            }
            catch(Exception ex)
            {
                Log.Error(ToString(), ex, this);
            }
        }
    }
}

You might thinking “Mike, there is more going on in here than just delegating to the base class’ Created() method and deleting the file. Well, you are correct — I had to do a few things further than what I thought I needed to do, and I’ll explain why.

I had to duplicate the logic — actually I wrote my own logic — to parse the Sitecore configuration which defines the substrings of file names and paths to ignore since these collections on the base UploadWatcher class are private — subclasses cannot access these collections — in order to prevent the code from deleting a file that should be ignored.

I also wedged in configuration with code to turn off the delete functionality if needed.

I then created the following patch include configuration file to hold the configuration setting to turn the delete functionality on/off:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <watchers>
      <media>
        <additionalSettings deleteAfterUpload="true" />
      </media>
    </watchers>
  </sitecore>
</configuration>

I then registered the new CleanupUploadWatcher in the Web.config — please see my previous post which explains why this is needed:

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">

      <!-- stuff here -->

      <!-- <add type="Sitecore.Resources.Media.UploadWatcher, Sitecore.Kernel" name="SitecoreUploadWatcher" /> -->
      <add type="Sitecore.Sandbox.Resources.Media.CleanupUploadWatcher, Sitecore.Sandbox" name="SitecoreUploadWatcher" />

      <!-- more stuff down here -->

    </modules>

    <!-- and more stuff down here -->
    
<system.webServer>

Let’s see this in action!

As you can see, there is no Media Library Item in the root of my Media Library (/sitecore/media library) — this is where the UploadWatcher uploads the file to:

cleanup-uploadwatcher-media-library-no-file

I then copied an image from a folder on my Desktop to the /upload directory of my Sitecore instance:

cleanup-uploadwatcher-move-file

As you can see above, the image is deleted after it is dropped.

I then went back to my Media Library and refreshed. As you can see here, the file was uploaded:

cleanup-uploadwatcher-media-library-file

If you have any thoughts on this or suggestions on making it better, please share in a comment.

Advertisement

Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: