Wednesday, October 17, 2007

Publish related media items


Often users are curious why when they publish articles, the media associated with this article was not published.

There is one simple explanation - media items/assets in Sitecore are considered as separate content items and should be published separately.

Thanks to the powerful Sitecore Workflow engine that can be easily extended using workflow actions, it is possible to publish related media items by means of a special workflow action.

So by simply adding a workflow action before the Auto Publish action with the logic below, you will ensure that the relations will be published before the actual item:

workflow_actions

The code of the workflow action:

using System;

using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data.Items;
using Sitecore.Links;
using Sitecore.Publishing;
using Sitecore.Workflows.Simple;

namespace WebApp.Customizations
{
    public class PublishRelationsAction
    {
        public void Process(WorkflowPipelineArgs args)
        {
            Item dataItem = args.DataItem;

            // Find all related items
            ItemLink[] itemLinks = dataItem.Links.GetValidLinks();

            foreach (ItemLink link in itemLinks)
            {
                Item itm = link.GetTargetItem();

                // publishing related media items - the ones that were referenced by the workflow item
                // this can be extended - you can publish related aliases also
                if (itm != null && itm.Paths.IsMediaItem)
                {
                    PublishItem(itm);
                }
            }
        }

        private void PublishItem(Item item)
        {
            PublishOptions options = new PublishOptions(PublishMode.SingleItem, item.Language, DateTime.Now);
            options.RootItem = item;
            options.Deep = false;
            options.SourceDatabase = item.Database;
            // publishing to the web database
            // production scenarios may have different publishing targets
            // this can be handled by some more advanced logic,
            // for example retrieving parameterized targets from the workflow action
            options.TargetDatabase = Factory.GetDatabase("web");
            new Publisher(options).Publish();
        }
    }
}

Related reading:

The caveats:

  1. If media items are associated with another workflow, they should be in the final state.
  2. Obviously this solution works when you are using workflow. Other solutions such as handing of publish:end events does not seem to be as easy as workflow action. In this case it could be better to introduce a one step workflow with one initial state "Editing" and final "Published" with such action attached.

Thanks to Alexander Tsvirchkov for investigation and code and Kim Hornung for ideas.

6 comments:

Kerry Bellerose said...

This is way cool, Alex! Thanks for writing this up. I bet a lot of customers will find this useful. Now we just need to make sure that everyone reads your blog, but they probably do already :-)

Angie said...

I'm getting an error when I set my sourcedatabase and the targetdatabase. It says it's readonly. Any thoughts on getting around this?

Also, I'm using vb, so I think I translated this line correctly,
New Publisher(options).Publish(), but I'm getting a syntax error (with no help from visual studio).

Angie said...

I have no idea if the last messages I posted you received. The web page keeps erroring out on me, so I apologize if you keep getting this!
I have a couple questions about this code. I converted it to VB.net, since that's what we are using. I had to change the options line to Dim options As PublishOptions = New PublishOptions(Factory.GetDatabase("master"), Factory.GetDatabase("web"), PublishMode.SingleItem, item.Language, DateTime.Now). The options.SourceDatabase = item.Database and options.TargetDatabase = Factory.GetDatabase("web") lines are giving an error stating they are read only. and the New Publisher(options).Publish() line gives a plain syntax error. Any thoughts on what I've got wrong? Thanks! ~Angie

Pallavi said...

hello,

i'm new to working with sitecore API and came across your post looking for custom workflow to publish aliases. Can you please let me know how can the above code be extended to publish aliases?

Thank You.

Alex Shyba said...

Hi Pallavi,

In order to handle aliases, you need to need to add another predicate into this IF statement that checks if an item is an alias:

if (itm != null && (itm.Paths.IsMediaItem ||(itm.TemplateID == Sitecore.TemplateIDs.Alias))
{
PublishItem(itm);
}

Alex Shyba said...

Also, check this post, it talks about a potentially better solution:
http://sitecoreblog.alexshyba.com/2010/07/hidden-feature-of-sitecore-62.html