Wednesday, July 21, 2010

Hidden feature of Sitecore 6.2


Too many times I’ve seen this question, it is not even funny. My images are not showing up when I publish an item. While it can easily be explained from the technical perspective and addressed, it does not always make sense from user’s point of view.
Today I’ve learnt that Sitecore 6.2 actually introduced a small feature, an additional processor for the publishItem pipeline, intended to process related media items linked from the FileDropArea field, which was also another addition in 6.2.


You may have already guessed. While it is only processing the links from FDA field, that does not mean we cannot teach it to process links from other fields as well.
using System.Collections.Generic;
using System.Linq;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Publishing;
using Sitecore.Publishing.Pipelines.PublishItem;

namespace Project.Shell.Pipelines.PublishItem
{
   public class AddAllItemReferences : Sitecore.Publishing.Pipelines.PublishItem.AddItemReferences
   {
      protected override List<Item> GetItemReferences(PublishItemContext context)
      {
         Assert.ArgumentNotNull(context, "context");
         var list = new List<Item>();
         // calling base method which processes links from FileDropArea field
         list.AddRange(base.GetItemReferences(context));

         // adding our "own" related items
         list.AddRange(GetRelatedReferences(context));
         return list;
      }

      protected virtual List<Item> GetRelatedReferences(PublishItemContext context)
      {
         Assert.ArgumentNotNull(context, "context");
         var relatedReferenceList = new List<Item>();
         if (context.PublishOptions.Mode == PublishMode.SingleItem)
         {
            var sourceItem = context.PublishHelper.GetSourceItem(context.ItemId);

            if (sourceItem.Paths.IsContentItem)
            {
               var itemLinks = sourceItem.Links.GetValidLinks();

               // adding only valid related items
               relatedReferenceList.AddRange(
                   itemLinks.Select(link => link.GetTargetItem()).Where(relatedItem => relatedItem != null));
            }
         }

         return relatedReferenceList;
      }
   }
}

Now all you need is replace the default AddItemReferences processor with the custom one:
<!--<processor type="Sitecore.Publishing.Pipelines.PublishItem.AddItemReferences, Sitecore.Kernel"/>—>
<processor type="Project.Shell.Pipelines.PublishItem.AddAllItemReferences, Project.Shell"/>
Too many blogs and articles were already posted about how to do it, but I still believe this is the easiest and most elegant way of solving this challenge.
http://sitecorejohn.spaces.live.com/Blog/cns!960125F1D4A59952!829.entry?wa=wsignin1.0&sa=4023005
http://sitecoreblog.alexshyba.com/2007/10/publish-related-media-items.html http://sdn.sitecore.net/FAQ/Media/Publish%20related%20media%20items.aspx
Enjoy!

6 comments:

Anonymous said...

Hi Alex, I believe this solution has some issues. Publishing a single item with this custom processor implemented, resulted in tens of thousands of items being published. I'm not sure what exactly is going on yet, but thought i'd let you know.

gijimmyj said...

Alex,

After implementing this we ran into a few issues:

1. publishing a single item results in thousands of items being published. I recycled the server at about 22,000 items. I've been trying to troubleshoot this, but haven't been able to find the problem.

2. doing a "smart" publish resulted in a fatal error:

'web'|#Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at myproject.processors.CustomPublishItemProcessor.GetRelatedReferences(PublishItemContext context) in C:\inetpub\sitecore\processors\CustomPublishItemProcessor.cs:line 32...

Any ideas?

Anonymous said...

I think you should add the same code the was on the AddItemReferences from the .net Reflector then add your code , this worked for thaaaaaaaaaaaaaanks for the post ,keep up the good posts :)

Matt said...

Hi Alex -

There is one issue with this solution in that it can cause the publisher to go into an infinite loop. As an example, Let's say you have two content items each that have reference fields pointing at the other. In that case, when you publish the first item, it will add the second item into the publishing queue . . . and then when the second item is processed in the queue, it will add the first item back in . . .

Is there a way to determine if an item is already in the queue? It would work much better if there was a way to check that before you add an item to the queue . . .

Julia812 said...

There is another option to get references published. Another publishing pipeline might be an option, the one that creates original list of items to be published.

<publish help="Processors should derive from Sitecore.Publishing.Pipelines.Publish.PublishProcessor">
<processor type="Sitecore.Publishing.Pipelines.Publish.AddLanguagesToQueue, Sitecore.Kernel" />
<processor type="Sitecore.Publishing.Pipelines.Publish.AddItemsToQueue, Sitecore.Kernel" />
<processor type="Custom.Publishing.AddItemReferencesToQueue, Custom" />
<processor type="Sitecore.Publishing.Pipelines.Publish.ProcessQueue, Sitecore.Kernel" />
</publish>

In the custom processor you can go through existing publishing queue, check for references and add them to the queue if they are not there already. That would prevent you from publishing duplicates.

Unknown said...

Julia's suggestion would take care of the infinite loop problem, however, would introduce a small issue. If you have "Publish Subitems" checked when publishing a content item, this would apply to all referenced items returned by AddItemReferencesToQueue. So there will be subitems from the published item and all the referenced items added to the queue, which may not be what you expect. Looks like the proper way of doing it is still use the publishItem pipeline, but check for the condition discovered by you guys.