13

Sitecore 8.1 update 2: Our content authors noticed that in a certain constellation newly uploaded images would not get published.

So the content author is in Experience Editor, adds a component which is saved as a data source below page (content page -> local content -> data source item with image field), uploads an image, links it in component and publishes the whole page, with options sub items and related items. There are no workflows yet so nothing should get lost, right? :)

Anyways. Everything is published (or in web database) except the image. If the component has a text field that is published as well. But when I publish the "local content" folder separately below the content page in content editor then the image will get published.

I could reproduce this issue in a vanilla version with the sample rendering and was wondering if this is by design or if it is a bug?

Update

Correct answer to the question is of course Dmytro Shevchenko's. A fix to the problem is in my answer.

  • I would file a ticket with Sitecore support, and let them know that you were able to reproduce this with a vanilla instance of 8.1u2. For the fastest support, include a description of the example scenario that you set up in your vanilla instance and/or a Sitecore package of content for them to test with. Definitely include their response as an answer to this post. – Zachary Kniebel Nov 21 '16 at 14:48
  • Is there any uncertainty left as to why the behavior occurs? Otherwise, in case you've received an explanation that helped you, please consider marking one of the answers as accepted. – Dmytro Shevchenko Nov 23 '16 at 10:29
  • @z00mable If you decide to mark your own answer as accepted, then please update it to include a detailed explanation of this publishing behavior. As it stands right now, it will not be very helpful to other users, being the first answer they see on this page. – Dmytro Shevchenko Nov 23 '16 at 10:44
13

When publishing with subitems and related items, there are two important aspects to keep in mind:

  • related items of subitems will be published;
  • related items of related items will not be published.

Comparing two publishing scenarios

Let's consider the relationships between your items:

Home
  A (page item) -----------------------+
    B (local content)                  |
      C (data source item)  <--(link)--+
                       |
Media library          |
  D (image) <--(link)--+

Item C (data source item) will be considered both a related item and a subitem of item A (page item).

  • When you publish item A, Sitecore will first publish all related items, including item C. The image item D will be treated as a related item of a related item, and hence will not be published.

    Even though item C is also a subitem of item A, it will be skipped when iterating over A's subitems, as it will have been published already.

  • When you publish item B directly, item C will be published as its subitem. So all related items of C, including item D, will be published in this scenario.

Conclusion

This behavior is kind of by design, but I would consider it a bug. In my opinion, Sitecore should change this behavior to always publish all related items of subitems, even if a certain subitem has already been published in the ongoing publishing process.

If you decide to submit this as a bug to Sitecore, please leave a comment here with the bug's reference number.

6

What Dmytro said is correct, Sitecore will only look at referenced items one level deep when publishing related items. What you need to do is have it also consider media items linked on those related items, then you'll catch the media items on your data sources. You can accomplish this if you override the logic that publishes the related items in the pipeline.

Take a look at the class Sitecore.Publishing.Pipelines.GetItemReferences.AddItemLinkReferences. You can reflect on this class and replace it in the pipeline with your own class that adds the logic to crawl the media items. For example, create a new class by copying what's in AddItemLinkReferences, then add this step when it's adding the linked items:

...
  foreach (Item obj in itemLinkArray.Select(link => link.GetTargetItem()).Where(relatedItem => relatedItem != null))
  {
    list.AddRange(PublishQueue.GetParents(obj));
    list.Add(obj);
    // This will look at the item's links looking for media items.
    list.AddRange(GetLinkedMediaItems(obj));
  }
  return list.Distinct(new ItemIdComparer());
}

Then add this method,

protected virtual List<Item> GetLinkedMediaItems(Item item)
{
  List<Item> mediaList = new List<Item>();
  ItemLink[] itemLinkArray = item.Links.GetValidLinks()
    .Where(link => item.Database.Name.Equals(link.TargetDatabaseName, StringComparison.OrdinalIgnoreCase))
    .ToArray();
  foreach (ItemLink link in itemLinkArray)
  {
    try
    {
      Item target = link.GetTargetItem();       
      if (target == null || !target.Paths.IsMediaItem) 
        continue;
      // add parent media items or folders
      Item parent = target.Parent;
      while(parent != null && parent.ID != ItemIDs.MediaLibraryRoot)
      {
        mediaList.Insert(0, parent);
        parent = parent.Parent;
      }
      mediaList.Add(target);
    }
    catch (Exception ex)
    {
      Log.Error("Error publishing reference link related media items", ex, typeof(AddItemAndMediaLinkReferences));
    }
  }
  return mediaList;
}

And the config to include it in your publish pipeline,

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getItemReferences>
        <processor type="Sitecore.SharedSource.Pipelines.Publish.AddItemAndMediaLinkReferences, Sitecore.SharedSource"
                   patch:instead="processor[@type='Sitecore.Publishing.Pipelines.GetItemReferences.AddItemLinkReferences, Sitecore.Kernel']"/>
      </getItemReferences>
    </pipelines>
  </sitecore>
</configuration>

Your problem inspired me to write a blog post about it: http://www.chrissulham.com/publish-sitecore-media-items-on-referenced-datasources/

  • That looks like a useful enhancement, thanks for sharing! As a side note, wouldn't it be easier to add a custom processor after AddItemLinkReferences that would simply invoke AddItemLinkReferences additionally for every related item? That would make Sitecore publish 2 levels of related items instead of 1. P.S. The name is spelled "Dmytro" ;-) – Dmytro Shevchenko Nov 22 '16 at 8:04
  • 1
    Sorry about the spelling! I fixed it. The simplest solution may be to re-run the same item link logic for each referring item, however I wanted to limit the scope of this to just media items. I'd have to test this, but I'd be worried that if we just re-ran the AddItemLinkReferences again on all linked items, we'd see a linked layout, then publish all the items using that layout for example. – Chris Sulham Nov 22 '16 at 14:21
  • That is a good point. If that is the case, then going the route you've taken will be more optimal. – Dmytro Shevchenko Nov 22 '16 at 14:37
5

I decided to contact Sitecore support. They are tracking this behavior as feature by design but opened a wish / feature request ticket for an option with deep publishing with related items. Reference number: 95121

Since I mentioned this post, they basically told me the same Dmytro Shevchenko had already stated in his answer and that Chris Sulham's fix tackles a slightly different task.

So they supplied an official sample fix, but mentioned it is not deeply tested.

Support:

  1. Create your custom processor based on the Sitecore.Publishing.Pipelines.GetItemReferences.AddItemLinkReferences one (i.e. copy sources of processor).
  2. Modify the GetReferences() method so that it would recursively process reference references if item is being referenced by the Renderings field (i.e. reference is a data source item).
  3. Replace predefined processor with your custom one.

Code Sample:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sitecore.Data;
using Sitecore.Data.Comparers;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Links;
using Sitecore.Publishing;
using Sitecore.Publishing.Pipelines.GetItemReferences;
using Sitecore.Publishing.Pipelines.Publish;
using Sitecore.Publishing.Pipelines.PublishItem;

namespace Sitecore.Example
{
  public class AddItemLinkReferences : GetItemReferencesProcessor
  {
    //Support
    private static readonly ID RenderingsFieldId = new ID("{F1A1FE9E-A60C-4DDB-A3A0-BB5B29FE732E}");
    private static readonly ID FinalRenderingsFieldId = new ID("{04BF00DB-F5FB-41F7-8AB7-22408372A981}");

    //Support
    private struct ReferenceInfo
    {
      public ReferenceInfo(Item referencedItem, ID sourceFieldId)
    : this()
      {
        ReferencedItem = referencedItem;
        SourceFieldId = sourceFieldId;
      }

      public Item ReferencedItem { get; private set; }
      public ID SourceFieldId { get; private set; }
    }

    protected override List<Item> GetItemReferences(PublishItemContext context)
    {
      Assert.ArgumentNotNull((object)context, "context");
      List<Item> list = new List<Item>();
      if (context.PublishOptions.Mode != PublishMode.SingleItem)
        return list;
      switch (context.Action)
      {
        case PublishAction.PublishSharedFields:
          Item sourceItem = context.PublishHelper.GetSourceItem(context.ItemId);
          if (sourceItem == null)
            return list;
          list.AddRange(this.GetReferences(sourceItem, true, new HashSet<ID>()));
          break;
        case PublishAction.PublishVersion:
          Item versionToPublish = context.VersionToPublish;
          if (versionToPublish == null)
            return list;
          list.AddRange(this.GetReferences(versionToPublish, false, new HashSet<ID>()));
          break;
        default:
          return list;
      }
      return list;
    }

    private IEnumerable<Item> GetReferences(Item item, bool sharedOnly, HashSet<ID> processedItems)
    {
      Assert.ArgumentNotNull(item, "item");

      processedItems.Add(item.ID);
      List<Item> resultReferences = new List<Item>();
      ItemLink[] references = item.Links.GetValidLinks().Where(link => item.Database.Name.Equals(link.TargetDatabaseName, StringComparison.OrdinalIgnoreCase)).ToArray();

      if (sharedOnly)
        references = references.Where(reference =>
        {
          Item sourceItem = reference.GetSourceItem();
          if (sourceItem == null)
            return false;
          if (!ID.IsNullOrEmpty(reference.SourceFieldID))
            return sourceItem.Fields[reference.SourceFieldID].Shared;
          return true;
        }).ToArray();

      foreach (var refItem in references.Select(link => new ReferenceInfo(link.GetTargetItem(), link.SourceFieldID)).Where(refInfo => refInfo.ReferencedItem != null))
      {
        //Support. Here we recursively add reference if wasn't added before and if referenced by the __Renderings field.
        if ((refItem.SourceFieldId == RenderingsFieldId || refItem.SourceFieldId == FinalRenderingsFieldId)  && !processedItems.Contains(refItem.ReferencedItem.ID))
        {
          resultReferences.AddRange(this.GetReferences(refItem.ReferencedItem, sharedOnly, processedItems));
        }

        resultReferences.AddRange(PublishQueue.GetParents(refItem.ReferencedItem));
        resultReferences.Add(refItem.ReferencedItem);
      }
      return resultReferences.Distinct(new ItemIdComparer());
    }
  }
}
-1

Related Items means only for the selected item. So if you click on the datasource item and publish that then the image will be published. Here is the exact definition from Sitecore (https://doc.sitecore.net/sitecore_experience_platform/content_authoring/publishing/publish_an_item_to_your_website):

To publish all the related items to the selected item, select Publish related items. Related items can be:

  • Clone references – items that are clones of the selected item.
  • Media references – media items that are related to the selected item.
  • Alias references – items that are aliases of the selected item.
  • The OP is publishing both the related items and the subitems. In this scenario, the related items of every subitem should also be published. – Dmytro Shevchenko Nov 21 '16 at 14:40
  • @DmytroShevchenko, that's not right because it is not the selected item. – Tamás Tárnok Nov 21 '16 at 14:41
  • You can test it yourself if you don't believe me. I verified how it works before answering this question. – Dmytro Shevchenko Nov 21 '16 at 14:42
  • @DmytroSchevchenko is correct that when you publish with subitems, it is supposed to treat each subitem as the "root item" (aka "selected item") when said subitem is published. TRNKTMS is also right about what is supposed to be considered a "related item", in terms of the quote from Sitecore's docs. As such, this sounds like a bug to me. – Zachary Kniebel Nov 21 '16 at 14:45
  • @ZacharyKniebel exactly, it supposed to be published, but as Sitecore documentation say it is only true for the selected item, so it is not a bug. they wanted to work on this way. – Tamás Tárnok Nov 21 '16 at 14:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.