DEV Community

Jesper Mayntzhusen
Jesper Mayntzhusen

Posted on

Assign images to content on upload in Umbraco

Let's say you are creating an Umbraco site that has fx a product catalogue that is imported. You may have a lot of different products that need images as well, more than you are interested in going through one by one.

Here is an example on how you can automate it, this example will use the Umbraco Starter kit as it already has a product catalogue with SKU's:
image

First thing first - let's limit the automation to a "Product Uploads" folder in the media library:

image

We will save the ID of this folder in a constants file:

namespace AutoImages.Constants
{
    public static class Content
    {
        public const int MediaUploadFolder = 1145;
    }
}
Enter fullscreen mode Exit fullscreen mode

Then we will create a media event to listen for uploads to that folder. For this we can use the cacherefresher event - it is the last event after media saving, plus this way we can trigger it for everything by rebuilding the cache:

using Umbraco.Core.Composing;
using Umbraco.Core.Services;
using Umbraco.Web.Cache;

namespace AutoImages.Events
{
    public class MediaEvents : IComponent
    {
        public void Initialize()
        {
            MediaCacheRefresher.CacheUpdated += MediaCacheRefresher_CacheUpdated;
        }

        public void Terminate()
        {
            MediaCacheRefresher.CacheUpdated -= MediaCacheRefresher_CacheUpdated;
        }

        private void MediaCacheRefresher_CacheUpdated(MediaCacheRefresher sender, Umbraco.Core.Cache.CacheRefresherEventArgs args)
        {
            // do stuff
        }        
    }
}
Enter fullscreen mode Exit fullscreen mode

Before diving into the event code we need to make sure it runs on the site, for that we need to register it in a composer:

using AutoImages.Events;
using Umbraco.Core;
using Umbraco.Core.Composing;

namespace AutoImages.Composers
{
    public class SiteComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            composition.Components().Append<MediaEvents>();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Alright so now that the basics are laid out - what we actually want to happen is something like this:

  1. Image is uploaded to our uploads folder
  2. That image has some sort of naming convention we can use to match it with the product nodes
  3. We search the product nodes to find a match
  4. If one is found we "select" the image on that node

To make it easy we will just say we will overwrite the product image if the name matches the sku:

using Examine;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Changes;
using Umbraco.Core.Sync;
using Umbraco.Web.Cache;

namespace AutoImages.Events
{
    public class MediaEvents : IComponent
    {
        private readonly IContentService _contentService;
        private readonly IMediaService _mediaService;
        private readonly IExamineManager _examineManager;

        public MediaEvents(IContentService contentService, IMediaService mediaService, IExamineManager examineManager)
        {
            _contentService = contentService;
            _mediaService = mediaService;
            _examineManager = examineManager;
        }

        private void MediaCacheRefresher_CacheUpdated(MediaCacheRefresher sender, Umbraco.Core.Cache.CacheRefresherEventArgs args)
        {
            if (args.MessageType != MessageType.RefreshByPayload)
                throw new NotSupportedException();

            var products = new Dictionary<string, string>();

            // for each media item the cache has refreshed
            foreach (var payload in (MediaCacheRefresher.JsonPayload[])args.MessageObject)
            {
                // only when the node is refreshed - so not on delete, move, etc
                if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshNode))
                {
                    var media = _mediaService.GetById(payload.Id);
                    if (media == null || media.Trashed) continue;

                    // only continue if the parent is the uploads folder
                    if (media.ParentId == Constants.Content.MediaUploadFolder)
                    {
                        // example name: UMB-BIKER-JACKET.png
                        var splitName = media.Name.Split('.');

                        // we save the sku and the media udi for later
                        products.Add(splitName[0], media.GetUdi().ToString());
                    }
                }
            }

            _examineManager.TryGetIndex(Umbraco.Core.Constants.UmbracoIndexes.ExternalIndexName, out IIndex index);
            var searcher = index.GetSearcher();

            foreach (var product in products)
            {
                var searchResults = searcher.CreateQuery().NativeQuery($"__NodeTypeAlias: product +sku: {product.Key}").Execute();

                if (searchResults.Any())
                {
                    foreach (var searchResult in searchResults)
                    {
                        if (!int.TryParse(searchResult.Id, out int contentId))
                            continue;

                        var node = _contentService.GetById(contentId);

                        // MediaPicker v2 saves the udi of the image, which was saved in the value of the product dictionary already
                        node.SetValue("photos", product.Value);
                        _contentService.SaveAndPublish(node);
                    }
                }
            }
        }

        public void Initialize()
        {
            MediaCacheRefresher.CacheUpdated += MediaCacheRefresher_CacheUpdated;
        }

        public void Terminate()
        {
            MediaCacheRefresher.CacheUpdated -= MediaCacheRefresher_CacheUpdated;
        }             
    }
}
Enter fullscreen mode Exit fullscreen mode

And here is a gif of it all working 😉

autoImages

That's it for now! I am not entirely happy with having to use services as much as I do, so if anyone know of a more performant way to do this please let me know!

It is however still way faster than having to do it all manually!

Top comments (0)