4

We have wrongly published 50,000 Components that are associated with more than one dynamic Component Template.

I need to unpublish the Components Presentations related to one particular dynamic Template. I have tried to unpublish the Component Template but it remains in the "Resolving" state and never ends.

What is the way to unpublish the presentations of Components of these 50,000 Components without altering the rest?

  • "it remains in the "Resolving" state" - I can only assume that resolving dependencies on 50K components will take a while. Have you tried enabling publish logging to see if it actually hangs, or if it's just really busy ? – Nuno Linhares Jan 29 '18 at 15:32
  • We have left it for 4 days and it remains in the "Resolving" state. We can publish more elements without problem being the template in the publication queue. – Jawad Jan 29 '18 at 15:49
  • OK, 4 days seems indeed extreme. Maybe script it? You can get the list of components with a where used, then unpublish the DCPs via script a few (100?) at a time? – Nuno Linhares Jan 29 '18 at 15:58
  • There are two things that I would recommend: 1. Check if there are any time outs in publisher logs 2. Perform database profiling to verify if there are any deadlocks or blocking. – Hiren Kaku Jan 29 '18 at 16:31
  • 1
    Use extreme caution if you're going to unpublish a list of components found from Where Used via script as suggested. Unpublishing the Component will unpublish ALL CPs using that Component including the ones you want, per docs.sdl.com/LiveContent/content/en-US/SDL%20Web-v1/… unless you have implemented something special. – Dave Forber Jan 29 '18 at 17:16
2

Unpublishing Components and Component Templates is a dangerous activity.

If you unpublish a Component, you will, by default, unpublish every presentation of that Component (including the ones you want!) and unpublish every page it is used on (including the homepage if it's on there).

If you unpublish a Component Template, you will, by default, unpublish all Dynamic Component Presentations using that template and every page that uses that CT (including the homepage if it's on there).

In this case, you cannot unpublish Components, since you will then have to republish them having changed the Component Template not to be associated with the given Schema. Also, it seems, that unpublishing the Component Template is taking ... rather a long time.

So, what to do?

I can't think of any solution in the GUI, so that leaves code.

I would suggest, as Nuno does in the comments, that you use a Core Service script to get a list of Components used by the Template and unpublish them, one by one. But that will of course unpublish the Components you do want, which is bad.

Before doing so, therefore, you will need to implement a Custom Resolver (see: https://docs.sdl.com/LiveContent/content/en-US/SDL%20Web-v1/GUID-F792AB1A-060A-4757-802D-3A2DB18F022D) to ensure you are only unpublishing the items using the template you want. The resolver will have to check the template each resolved item is using and remove. Something like this (I have not tested, this is just modified from one of our existing resolvers, so due diligence, checking, etc.):

class GetMeOutOfJailResolver : IResolver
{
    public void Resolve(IdentifiableObject item, ResolveInstruction instruction, PublishContext publishContext, ISet<ResolvedItem> resolvedItems)
    {
        Session session = publishContext.Session;
        int SCHEMA_ITEM_ID = 123456;  //i.e. 123456 in tcm:12-123456-8 to cater for localizations
        int TEMPLATE_ITEM_ID = 345678;
        string USER_ID = "tcm:my-user-id";

        if (item is Component
            && instruction.Purpose == ResolvePurpose.UnPublish
            && session.User.Id == USER_ID
            //&& other safeguards
        )
        {
            Component component = (Component)item;
            if (component.Schema.Id.ItemId == SCHEMA_ITEM_ID)
            {
                ResolvedItem[] originalResolvedItems = new ResolvedItem[resolvedItems.Count];
                resolvedItems.CopyTo(originalResolvedItems, 0);

                foreach (ResolvedItem resolvedItem in originalResolvedItems)
                {
                    if (resolvedItem.IsDynamicComponentPresentation
                        && resolvedItem.Template.Id.ItemId != TEMPLATE_ITEM_ID)
                    {
                        resolvedItems.Remove(resolvedItem);
                    }
                }
            }
        }
    }
}

Obviously a resolver is called every time something is published or unpublished. Add whatever safeguards you need to (be it only allowing a particular user to use the resolver, implementing some change freeze across the system while the resolver is active, or whatever).

Sorry, I don't see any easier or more elegant options. It would be nice to see this as a product feature ((Un)publish -> choose Component Presentations to (Un)publish).

| improve this answer | |
  • The problem here is how to unpublish a group of components, for example send to unpublish 10,000 components then another 10,000 ... – Jawad Feb 4 '18 at 14:34
0

Actually Resolving - state - still working status maybe you will get timeout due to 50k items.

Alternatively, you can try to find and unpublish the DCP components by using core'service either by search and find on folders components or by DCP component template.

Here you can find the working example code to find by ComponentTemplateID to unpublish the components.

You can modify this code to implement the batchSize to publish the items in the queue.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel;
    using System.Xml;
    using Tridion.ContentManager.CoreService.Client;

    namespace FindAndUnPublishDCPs
    {
        class Program
        {
            private const string ServiceUrl = "{0}webservices/CoreService201603.svc/basicHttp";
            private const int MaxMessageSize = 10485760;
static void Main(string[] args)
            {
                string componentTemplateId = "tcm:5-333-32"; //change this value to your environment Id
                string targetId = "tcm:0-2-65538"; //change this value to your enviorment targetId

                try
                {
                        var client = GetClient("cms-hostname");

                        client.ChannelFactory.Credentials.Windows.ClientCredential =
                               new System.Net.NetworkCredential(@"domain\username", "password");

                        Console.WriteLine("Current User:" + client.GetCurrentUser().Title);

                        var resolveInstruction = new ResolveInstructionData { IncludeChildPublications = false, Purpose = ResolvePurpose.RePublish };

                        var resolvedContexts = client.ResolveItems(new[] { componentTemplateId }, resolveInstruction, new[] { targetId }, new ReadOptions());

                        List<ComponentPresentation> componentPresentations = (
                            from resolvedContext in resolvedContexts
                            from resolvedItem in resolvedContext.ResolvedItems
                            select new ComponentPresentation
                            {
                                Component = resolvedItem.Item,
                                Template = resolvedItem.Template
                            }).ToList();

                        UnPublishInstructionData instruction = new UnPublishInstructionData
                        {
                            ResolveInstruction = new ResolveInstructionData(),
                        };

                        foreach (var cp in componentPresentations)
                        {
                            client.UnPublish(new[] { cp.Component.IdRef }, instruction, new[] { targetId }, PublishPriority.Normal, new ReadOptions { LoadFlags = LoadFlags.None });
                        }
                }
                catch (Exception ex)
                {
                    Console.Write($"Failed to process resolve published Component Presentations based on Id: {componentTemplateId}", ex.Message);
                }
            }

            private class ComponentPresentation
            {
                public LinkToIdentifiableObjectData Component { get; set; }

                public LinkToTemplateData Template { get; set; }
            }

            static CoreServiceClient GetClient(string hostname)
            {
                var basicHttpBinding = new BasicHttpBinding
                {
                    MaxReceivedMessageSize = MaxMessageSize,
                    ReaderQuotas = new XmlDictionaryReaderQuotas
                    {
                        MaxStringContentLength = MaxMessageSize,
                        MaxArrayLength = MaxMessageSize
                    },
                    Security = new BasicHttpSecurity
                    {
                        Mode = BasicHttpSecurityMode.TransportCredentialOnly,
                        Transport = new HttpTransportSecurity
                        {
                            ClientCredentialType = HttpClientCredentialType.Windows
                        }
                    }
                };

                hostname = string.Format("{0}{1}{2}", hostname.StartsWith("http") ? "" : "http://", hostname, hostname.EndsWith("/") ? "" : "/");

                var remoteAddress = new EndpointAddress(string.Format(ServiceUrl, hostname));
                return new CoreServiceClient(basicHttpBinding, remoteAddress);
            }
        }
    }

Or try to find and search the items by folder level to filter the components and then check those items are published or not, sent the items to publish queue for unpublish.

I hope it helps.

| improve this answer | |
  • When you execute this instruction (client.UnPublish (new [] {cp.Component.IdRef}, instruction, new [] {targetId}, PublishPriority.Normal, new ReadOptions {LoadFlags = LoadFlags.None})), you would be sending the component to publish by which all component presentations will be unpublished, which I want to avoid. – Jawad Jan 31 '18 at 15:35
  • But your requirement to unpublish those DCP components of used DCP template right? – Velmurugan Jan 31 '18 at 15:50
  • For each component, unpublish only one of many component presentation of this component. – Jawad Jan 31 '18 at 16:00
  • The problem here is how to unpublish a group of components, for example send to unpublish 10,000 components then another 10,000 ... – Jawad Feb 4 '18 at 14:34
  • I'm thinking you would manage a set of items the same way a user would -- by noting the specific items, using folders (recursively), or optionally creating Bundles for each set of items (but programmatically). Once a set is done, do the next set. Since it's a specific Schema, maybe you could group Components by starting character (all the "A's" first). – Alvin Reyes Feb 4 '18 at 16:20

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.