Creating archive items in media library from media items in a Sitecore Scheduled Task

We had, in our project, documents uploaded pertaining to different document categories for each product in a product repository. On the product page – the user was to have the capability to download an archive / zip file of all documents pertaining to a product – for a given document category.

We had to dynamically generate the archive, based on the documents uploaded in media library and associated with the products.

We initially looked into generating the archive file on the fly when a user requested for a download. However this was not too efficient, especially when the files were larger in size. The wait time was far longer than acceptable.

So as a solution, we decided to create a scheduled task – which would

  • loop through the products, locate the media items associated with each product – each document category
  • create an archive of the group
  • save it as a new media item – with an indicator as to which product & document category it was associated with

In our implementation then, when a user requested for an archive download, we simply located the corresponding media library archive item based on the product & document category indicators, and output the download link!

    public class ProductDownloadsArchiveCreator
    {
        public void Execute(Item[] items, Sitecore.Tasks.CommandItem command, Sitecore.Tasks.ScheduleItem schedule)
        {
            try
            {
                Sitecore.Diagnostics.Log.Info("ProductDownloadsArchiveCreator :: Job Started", this);

                using (new BulkUpdateContext())
                {
                    IDocumentRepository _documentRepository = DocumentRepositoryFactory.Build();
                    Item archiveRootFolder =
                        SitecoreHelper.ItemMethods.GetItemFromGUIDInMaster(Constants.ItemGuids
                            .ProductDownloadsArchiveFolder);

                    archiveRootFolder.DeleteChildren();

                    List productDocuments = _documentRepository.GetAllProductDocuments();

                    List productsWithDocuments = GetProductGuidsWithDocuments(productDocuments);

                    if (!productsWithDocuments.Any()) return;

                    foreach (Guid productGuid in productsWithDocuments)
                    {
                        List currentProductDocuments =
                            productDocuments
                                .Where(d => d.MediaProducts != null && d.MediaProducts.Contains(productGuid)).ToList();

                        ProcessCurrentProductDocuments(currentProductDocuments, productGuid);
                    }
                }

                PublishArchiveFolder();

                Sitecore.Diagnostics.Log.Info("ProductDownloadsArchiveCreator :: Job Ended", this);
            }
            catch (Exception e)
            {
                Sitecore.Diagnostics.Log.Error("ProductDownloadsArchiveCreator :: Job Error", e, this);
            }
        }

        #region Private Methods

        private List GetProductGuidsWithDocuments(List productDocuments)
        {
            List productsWithDocuments = new List();

            foreach (DocumentResultItem document in productDocuments)
            {
                if (document.MediaProducts != null && document.MediaProducts.Any())
                {
                    productsWithDocuments.AddRange(document.MediaProducts);
                }
            }

            return productsWithDocuments.Distinct().ToList();
        }

        private void ProcessCurrentProductDocuments(List currentProfileDocuments, Guid productGuid)
        {
            try
            {
                List documentCategories = new List();
                foreach (DocumentResultItem document in currentProfileDocuments)
                {
                    if (document.MediaCategories != null && document.MediaCategories.Any())
                    {
                        documentCategories.AddRange(document.MediaCategories);
                    }
                }

                documentCategories = documentCategories.Distinct().ToList();

                foreach (Guid documentCategoryGuid in documentCategories)
                {
                    List currentProductCategoryDocuments =
                        currentProfileDocuments.Where(d =>
                            d.MediaCategories != null && d.MediaCategories.Contains(documentCategoryGuid)).ToList();

                    List currentProductCategoryClassifications =
                        currentProductCategoryDocuments.Select(d => d.DocumentClassification)
                            .Where(c => !string.IsNullOrWhiteSpace(c) && c.Trim() != string.Empty).Select(c => c.Trim())
                            .Distinct().ToList();

                    currentProductCategoryClassifications.Add(string.Empty);

                    foreach (string classification in currentProductCategoryClassifications)
                    {
                        List currentArchiveFiles = currentProductCategoryDocuments
                            .Where(d => string.IsNullOrWhiteSpace(classification)
                                ? string.IsNullOrWhiteSpace(d.DocumentClassification)
                                : d.DocumentClassification == classification).ToList();

                        CreateArchive(currentArchiveFiles, productGuid, documentCategoryGuid, classification);
                    }
                }
            }
            catch (Exception e)
            {
                Sitecore.Diagnostics.Log.Error
                    ("ProductDownloadsArchiveCreator :: ProcessCurrentProductDocuments :: Exception " +
                     $"{e.StackTrace} for Product '{productGuid}'", this);
            }
        }

        private void CreateArchive
            (List currentArchiveFiles, Guid productGuid, Guid documentCategoryGuid, string classification)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
                {
                    foreach (DocumentResultItem currentArchiveFile in currentArchiveFiles)
                    {
                        Item mediaItem = SitecoreHelper.ItemMethods.GetItemFromGUIDInMaster(currentArchiveFile.ItemId.ToString());
                        if (mediaItem != null)
                        {
                            Media media = MediaManager.GetMedia(mediaItem);

                            Stream stream = media?.GetStream()?.Stream;

                            if (stream != null)
                            {
                                ZipArchiveEntry currentFile = archive
                                    .CreateEntry(mediaItem.Name + "." +
                                                 SitecoreHelper.ItemRenderMethods.GetRawValueByFieldName(
                                                     "Extension",
                                                     mediaItem, false));

                                using (Stream entryStream = currentFile.Open())
                                {
                                    stream.CopyTo(entryStream);
                                    entryStream.Flush();
                                }
                            }
                        }
                    }
                }

                if (memoryStream.Length == 0)
                {
                    Sitecore.Diagnostics.Log.Error
                        ($"ProductDownloadsArchiveCreator :: CreateArchive :: Steam empty for Product '{productGuid}" +
                         $"', DocumentCategory '{documentCategoryGuid}', Classification '{classification}'", this);
                }

                memoryStream.Seek(0, SeekOrigin.Begin);
                SaveToSitecoreMediaLibrary(memoryStream, currentArchiveFiles, productGuid, documentCategoryGuid, classification);
            }
        }

        private void SaveToSitecoreMediaLibrary
            (MemoryStream memoryStream, List currentArchiveFiles,
            Guid productGuid, Guid documentCategoryGuid, string classification)
        {
            Item productItem = SitecoreHelper.ItemMethods.GetItemFromGUIDInMaster(productGuid.ToString());
            Item documentCategoryItem = SitecoreHelper.ItemMethods.GetItemFromGUIDInMaster(documentCategoryGuid.ToString());
            string cleanProductCode = productItem.Fields["Product Code"].Value.ToAlphanumeric();

            string fileTitle = cleanProductCode.ToLower().Trim() + "_" + documentCategoryItem.Name.ToAlphanumeric()
                + (string.IsNullOrWhiteSpace(classification) ? "" : "_" + classification.ToAlphanumeric());
            Item productDownloadsArchiveFolder = SitecoreHelper.ItemMethods.GetItemFromGUIDInMaster(Constants.ItemGuids.ProductDownloadsArchiveFolder);

            MediaCreatorOptions options = new MediaCreatorOptions
            {
                FileBased = false,
                IncludeExtensionInItemName = false,
                OverwriteExisting = true,
                Versioned = false,
                Destination = productDownloadsArchiveFolder.Paths.FullPath + "/"
                                  + cleanProductCode.ToUpper()[0] + "/"
                                  + cleanProductCode.ToLower() + "/"
                                  + fileTitle,
                Database = Sitecore.Configuration.Factory.GetDatabase("master")
            };
            Item mediaItem = MediaManager.Creator.CreateFromStream(memoryStream, fileTitle, options);

            Sitecore.Diagnostics.Log.Info("ProductDownloadsArchiveCreator :: Archive Created - " + mediaItem.Paths.FullPath, this);

            mediaItem.Editing.BeginEdit();
            mediaItem["Document Classification"] = string.IsNullOrWhiteSpace(classification) ? "none" : classification;
            mediaItem["Description"] = productGuid.ToString("N") + "|" + documentCategoryGuid.ToString("N");
            string archiveFiles = "";
            foreach (DocumentResultItem file in currentArchiveFiles)
            {
                archiveFiles += file.ItemId + "|";
            }
            mediaItem["Keywords"] = archiveFiles.Trim('|');
            mediaItem["Extension"] = "zip";
            mediaItem.Editing.EndEdit();
        }

        private void PublishArchiveFolder()
        {
            Language language = Sitecore.Context.Language;
            DateTime publishedTime = DateTime.Now;

            Item archivesFolder = SitecoreHelper.ItemMethods.GetItemFromGUIDInMaster(Constants.ItemGuids.ProductDownloadsArchiveFolder);

            ItemList publishingTargets = PublishManager.GetPublishingTargets(archivesFolder.Database);

            foreach (Item publishingTarget in publishingTargets)
            {
                string targetDatabaseName = publishingTarget["Target database"];
                if (string.IsNullOrEmpty(targetDatabaseName))
                    continue;

                Sitecore.Diagnostics.Log.Info("ProductDownloadsArchiveCreator :: Publishing to " + targetDatabaseName, this);

                Database targetDatabase = Sitecore.Configuration.Factory.GetDatabase(targetDatabaseName);
                if (targetDatabase == null)
                    continue;

                PublishOptions publishOptions = new PublishOptions
                    (archivesFolder.Database, targetDatabase, PublishMode.Smart, language, publishedTime);
                Publisher publisher = new Publisher(publishOptions);
                publisher.Options.RootItem = archivesFolder;
                publisher.Options.Deep = true;
                publisher.Publish();
            }
        }

        #endregion
    }

Leave a Reply

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 )

Google photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s