1

I have a requirement in a project where a service is automatically creating media items in sitecore media library in a random folder structure. I want to publish the media items once they are created. Once media item is created, I have the media id and path to do the publish but, how to automatically publish all the parent folders as well which are also created in the process.

Like my folder structure can be like below :

/sitecore/Media Library/Media/Images/Image1

/sitecore/Media Library/Media/Image2

/sitecore/Media Library/Media/PDF/Pdf1

3
  • So you want to find the one common parent folder to all new items, so that you only need to issue one publish command (with sub items)? i.e. in your example "Sitecore/Media Library/Media"? Jul 6, 2023 at 20:29
  • I want to find the one folder which already does not exist in webDb and do publish from there. Like for first call, It should find Media and publish all subitems. For next call, It just publish Image 2. Likewise if there is subsequent request to create say Media/ImagesNew/Image3 then it finds ImagesNew and do publish from there. Jul 6, 2023 at 20:58
  • Basically, if I just publish the item, it will not automatically publish the parent folders which are created by the service so, I want to publish the media item along with their new parent folders created Jul 6, 2023 at 21:06

4 Answers 4

1

You first need to find the ancestor or self item which is not in web database yet and then publish it with subitems.

Remember to check if the media item is not there already - this may happen if folder containing that item was already published with subitems while publishing another media item:

public void PublishMediaItemsAndTheirFolders()
{
    List<MediaItem> createdMediaItems = UploadMediaItems();
    var targetDatabase = Database.GetDatabase("web");

    foreach (var createdMediaItem in createdMediaItems)
    {
        Item itemToBePublished = FindNotPublishedAncestorOrSelf(createdMediaItem.InnerItem, targetDatabase);

        if (itemToBePublished != null)
        {
            var handle = PublishManager.PublishItem(itemToBePublished, new[] {targetDatabase}, new[] {itemToBePublished.Language}, true, false, false);
            // wait 3 minutes - if publishing job takes longer, it will process in the background
            PublishManager.WaitFor(handle, 3 * 60 * 1000, 100);
        }
    }
}

private Item FindNotPublishedAncestorOrSelf(Item item, Database targetDatabase)
{
    // this may happen if item was already published when publishing parent folder
    if (targetDatabase.GetItem(item.ID, item.Language, item.Version)?.Statistics.Revision == item.Statistics.Revision)
        return null;

    // parent already is in web database - it's enough to publish item itself
    if (targetDatabase.GetItem(item.ParentID) != null)
        return item;

    // parent not in web database yet - check if parent of the parent is there already
    return FindNotPublishedAncestorOrSelf(item.Parent, targetDatabase);
}
0

There are 2 options to publish an item programmatically.

Option 1:

You can use the code below from this post which will publish it from your C# code or whatever service is automatically creating the media items.

using (new Sitecore.SecurityModel.SecurityDisabler())
{
        string itemId = "E7A2DE22-4338-49A6-840F-4B3124F1FFBD";
    
        Database webdb = Sitecore.Configuration.Factory.GetDatabase("web");
        Database masterdb = Sitecore.Configuration.Factory.GetDatabase("master");
    
        ClearSitecoreDatabaseCache(masterdb);
    
        Item masterItem = masterdb.GetItem(new ID(itemId));
    
        // target databases
        Database[] databases = new Database[1] { webdb };
    
        Sitecore.Handle publishHandle = Sitecore.Publishing.PublishManager.PublishItem(masterItem, databases, webdb.Languages, true, false);
    
        ClearSitecoreDatabaseCache(webdb);
}

Option 2: To use Sitecore Powershell extensions Publish-Item from your service code

You can follow this link for more details - https://doc.sitecorepowershell.com/appendix/common/publish-item

0

The very basic code to get all the parents of an item is to create a recursive function that will loop through to the parent. It is described in the Sitecore documents. Here is the code.

ProcessRecursively(home.Parent);
    
private void ProcessRecursively(Sitecore.Data.Items.Item item)
{
    if (item.Parent != null )
    {
        // Process parent item
        ProcessRecursively(item.Parent);
    }
}

Refer to this link.

https://doc.sitecore.com/xp/en/developers/93/sitecore-experience-manager/access-the-ancestors-of-an-item.html

Now you can pass your item ID in the function and if the item.Parent is not null then you can write the logic to publish the item using this function.

public void PublishItem(Database dbMaster, Database dbWeb, Item iParent)
{
    try
    {
        PublishOptions po = new PublishOptions(dbMaster, dbWeb, PublishMode.SingleItem, Sitecore.Context.Language, DateTime.Now);
        po.RootItem = iParent;
        po.Deep = true; // Publishing subitems

        (new Publisher(po)).Publish();
    }
    catch (Exception ex)
    {
        Sitecore.Diagnostics.Log.Error("Exception publishing items from custom pipeline! : " + ex, this);
    }
}

Note: If you want to check that the item is already published in the Web Database then you can take the code snippets given in the answer by @Dean and make a check of it.

Hope this will give you an idea.

1
  • Thank you! This is a possible solution. Jul 7, 2023 at 16:24
0

OK so your requirement is to recursively check each parent item to find the deepest 'unpublished' parent, then publish.

You can do this by simply trying to grab the item from the web database. If the item is not null then it exists.

You could try something like below (note I have tested this):

var master = Sitecore.Configuration.Factory.GetDatabase("master");
var yourItem = master.GetItem("/sitecore/content/path/to/item");
var itemToPublish = FindUnpublishedItem(yourItem);
if(IsPubishable(itemToPublish))
{
    SmartPublishToTarget(itemToPublish, master, true);
}
public Item FindUnpublishedItem(Item item)
{
    var web = Sitecore.Configuration.Factory.GetDatabase("web");
    var parentInWeb = web.GetItem(item.ParentID);
    if (parentInWeb == null) return FindUnpublishedItem(item.Parent);
    return item;
}
private void SmartPublishToTarget(Item item, Database master, bool publishDescendants)
{
    var publishToTargetUser = @"sitecore\PublishToTargetUser";
    if (Sitecore.Security.Accounts.User.Exists(publishToTargetUser))
    {
        var scUser = Sitecore.Security.Accounts.User.FromName(publishToTargetUser, true);
        using (new Sitecore.Security.Accounts.UserSwitcher(scUser))
        {
            var targetDb = Sitecore.Configuration.Factory.GetDatabase("web");

            var publishOptions = new Sitecore.Publishing.PublishOptions(master,
                targetDb,
                Sitecore.Publishing.PublishMode.Smart,
                item.Language,
                DateTime.Now);

            var publisher = new Sitecore.Publishing.Publisher(publishOptions);
            publisher.Options.RootItem = item;
            publisher.Options.Deep = publishDescendants;
            publisher.Publish();
        }
    }
    else
    {
        throw new UnauthorizedAccessException("publishToTargetUser does not appear to be setup in sitecore");
    }
}
private bool IsPubishable(Item item)
{
    return item.Publishing.IsPublishable(DateTime.Now, false);
}

Note: I believe it is better practice to use UserSwitcher() rather than SecurityDisabler(). This allows you to lock down only the required actions needed for this task. In order for it to work though, you will need tocreate the user in sitecore and assign permissions to access and publish the specific area in content tree.

1
  • Than you so much! This is a possible solution Jul 7, 2023 at 16:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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