Unlocking the Potential of Sitecore Content Hub: Filtering Locked Assets with a Custom Azure Function

Introduction

Sitecore Content Hub is a powerful and flexible platform that helps organizations manage their digital assets efficiently. However, like any software, it may have certain limitations or quirks that users need to work around. In this blog post, we'll explore a common problem faced by Sitecore Content Hub users and provide an elegant solution that leverages the power of Azure Functions to filter locked assets from download orders.

The Problem

Imagine you're managing a large repository of digital assets in Sitecore Content Hub. You need to exclude assets marked as "isLocked = True" from download results and zip packages. However, you notice that the default filters on the "Download Order" page don't always produce consistent results. Further investigation reveals that in Sitecore Content Hub 4.2, the Selection on M.Asset using Order is unavailable, causing it to default to Archive.

The Solution

Fear not, dear content managers and developers! We've got a custom solution that will not only resolve the issue but also make your Sitecore Content Hub experience smoother. We'll create a custom filter to remove Assets with the field value "isLocked" set to True by modifying the entity through entity management ("/admin/entitymgmt/entity/51935"). We'll copy the default "System settings" and paste them into "Custom Settings" before saving the search component. Then, we'll add our filter for locked items. This workaround solves the problem, but we can make it even better by employing an Azure Function to ensure that locked items are not included in the zip output.

The Code: Unleashing the Power of Azure Functions

Our custom Azure Function, H_RemoveLockedAssetsFromDownloadOrder, is designed to filter out locked assets from download orders. It inherits from BaseTrigger and is triggered by an Action in response to a specific event. The function retrieves the target entity and its related job description, extracting the download order job description configuration as a JObject. It then iterates through the JArray in reverse, checking the lock status of each asset. If an asset is locked, it is removed from the array. The modified JArray is saved back into the job description configuration, and the processing job is resumed. By leveraging the power of Azure Functions, our custom solution provides a more reliable and efficient way to manage assets in Sitecore Content Hub. It's a win-win situation for both developers and managers alike.

using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc;

using Microsoft.Azure.WebJobs;

using Microsoft.Azure.WebJobs.Extensions.Http;

using Microsoft.AspNetCore.Http;

using Microsoft.Extensions.Logging;

using Sitecore.CH.Base.Services;

using Stylelabs.M.Sdk.Contracts.Base;

using Stylelabs.M.Framework.Essentials.LoadConfigurations;

using Sitecore.CH.Implementation.Services;

using System.Net.Http;

using Newtonsoft.Json.Linq;

using Stylelabs.M.Framework.Essentials.LoadOptions;

using System.Text;

using Microsoft.Identity.Client;


namespace Sitecore.CH.Implementation.AzFunctions.Functions

{

    public class H_RemoveLockedAssetsFromDownloadOrder : BaseTrigger

    {

        public H_RemoveLockedAssetsFromDownloadOrder(IMClientFactory mClientFactory,

                                        ILoggerService<H_SampleTriggerFunction> logger,

                                        ILoggingContextService loggingContextService) : base(mClientFactory, logger, loggingContextService)

        {

        }


        [FunctionName("H_RemoveLockedAssetsFromDownloadOrder")]

        public Task<IActionResult> EntryPoint([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log)

        {

            return base.Run(req);

        }


        protected override async Task<IActionResult> Execute(HttpRequest req, IEntity entity)

        {

            _logger.LogInformation("Starting execution of H_RemoveLockedAssetsFromDownloadOrder...");


            var targetEntity = await _mClientFactory.Client.Entities.GetAsync(entity.Id.Value);

            await targetEntity.LoadRelationsAsync(new RelationLoadOption("JobToJobDescription")).ConfigureAwait(false);

            IParentToOneChildRelation jobToJobDescription = targetEntity.GetRelation<IParentToOneChildRelation>("JobToJobDescription");


            var id = jobToJobDescription.GetId().Value;

            IEntity jobDescription = await _mClientFactory.Client.Entities.GetAsync(id);


            // get download order job description configuration

            var config = await jobDescription.GetPropertyValueAsync("Job.Configuration"as JObject; ;

            JArray jarrayObj = config["entity"].ToObject<JArray>();


            _logger.LogInformation($"Initial assets count: {jarrayObj.Count}");



            // Remove the locked assets

            for (int i = jarrayObj.Count - 1; i >= 0; i--)

            {

                var assetID = jarrayObj[i].Value<long>();

                var asset = await _mClientFactory.Client.Entities.GetAsync(assetID);


                bool isLocked = await GetAssetLockStatus(asset);


                if (isLocked)

                {

                    _logger.LogInformation($"Removing locked asset with ID: {assetID}");

                    jarrayObj.RemoveAt(i);

                }

            }


            _logger.LogInformation($"Filtered out locked assets. Remaining assets count: {jarrayObj.Count}");


            config["entity"] = jarrayObj;

            jobDescription.SetPropertyValue("Job.Configuration", config);

            await _mClientFactory.Client.Entities.SaveAsync(jobDescription);


            // resume processing job

            targetEntity.SetPropertyValue("Job.State""Pending");

            await _mClientFactory.Client.Entities.SaveAsync(targetEntity);


            _logger.LogInformation("Execution of H_RemoveLockedAssetsFromDownloadOrder completed.");


            return new OkResult();

        }

        private async Task<bool> GetAssetLockStatus(IEntity asset)

        {

            await asset.LoadPropertiesAsync(new PropertyLoadOption("Dml.IsLocked")).ConfigureAwait(false);

            var isLocked = await asset.GetPropertyValueAsync<bool>("Dml.IsLocked");

            return isLocked;

        }

        protected override IEntityLoadConfiguration GetEntityLoadConfiguration()

        {

            return EntityLoadConfiguration.Default;

        }


        protected override Task<bool> IsValid(IEntity entity)

        {

            return Task.FromResult(true);

        }

    }

}

Conclusion

In the ever-evolving world of digital asset management, it's crucial to have the flexibility to work around limitations in software platforms. Our custom Azure Function solution for filtering locked assets in Sitecore Content Hub is a testament to the power of innovative thinking and the potential of cloud-based serverless computing. So, whether you're a developer seeking a more efficient way to manage assets or a manager looking for a seamless Sitecore Content Hub experience, this custom solution is a game-changer. With Azure Functions at your disposal, the possibilities are truly endless. Happy asset management!


Comments