1

I have successfully created a web template as a farm solution on a SharePoint 2013 on premises farm and can use it to deploy site collections as well as subsites.

In the web template I have used a copy of the teamsite onet.xml and have added all the standard teamsite features including ones that would normally be stapled to the site feature and web feature sections.

I have removed any lists / libraries from being provisioned in the onet.xml.

I have created 2 web scoped features. The first one provisions a set of libraries programmatically.

The second one, which is also the last web feature in the onet.xml is supposed to delete all entries from the quick launch then add back in my custom entries.

When I run this feature in an existing site it does what it is supposed to.

When it runs as part of the site creation process it adds my quick launch entries but there seem to be other ones that are added in by SharePoint afterwards.

I am wondering if either my 'clearing' of the quicklaunch is failing inside the site creation process or if certain items are added to the quicklaunch after web features in a web template ?

I have included the code that I am using. Apologies if it should have been abbreviated..

public class KG2013Core_QLaunch_CaymanOLEventReceiver : SPFeatureReceiver
{
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        //custom extension to get web in feature.
        using (SPWeb spWeb = properties.GetWeb() as SPWeb)
        {                
            SPSite spSite = properties.GetSite() as SPSite;
            SPWeb spRootWeb = spSite.RootWeb;               

            //configure the quicklaunch menu
            configureQuickLaunch(spWeb);             
        }
    }       

    public static void configureQuickLaunch(SPWeb spWeb)
    {            
        ClearQuickLaunch(spWeb);
        AddQuickLaunchList(spWeb,"Engagement Admin", "Engagement Tasks");
        AddQuickLaunchList(spWeb,"Engagement Admin", "Engagement Calendar");
    }

    public static void ClearQuickLaunch(SPWeb spWeb)
    {
        SPNavigationNodeCollection quickLaunch = spWeb.Navigation.QuickLaunch;

        for (int i = quickLaunch.Count - 1; i > -1; i--)
        {
            quickLaunch[i].Delete();
        }

        quickLaunch.Parent.Update();
        spWeb.Update();
    }

    public static void AddQuickLaunchList(SPWeb spWeb, string header, string listname)
    {
        SPNavigationNodeCollection quickLaunch = spWeb.Navigation.QuickLaunch;

        // try to get the list
        SPList list = spWeb.Lists.TryGetList(listname);

        if (list != null)
        {
            string name = list.Title;
            string url = list.DefaultViewUrl;

            // NOTE : quickLaunch.Count provided the wrong number.  Probably due to active instance of web object.
            if (quickLaunch.Parent.Children.Count > 0)
            {
                if (header != null)
                {
                    //try to get quick launch header
                    SPNavigationNode nodeHeader = quickLaunch.Parent.Children.Cast<SPNavigationNode>().Where(n => n.Title == header).FirstOrDefault();

                    //if header not found create it
                    if (nodeHeader == null)
                    {
                        nodeHeader = quickLaunch.AddAsLast(new SPNavigationNode(header, ""));
                        nodeHeader.Update();
                    }

                    //try to get node item under header
                    SPNavigationNode nodeItem = nodeHeader.Children.Cast<SPNavigationNode>().Where(n => n.Title == name).FirstOrDefault();

                    //If item not found under heading then create it
                    if (nodeItem == null)
                    {
                        nodeItem = nodeHeader.Children.AddAsLast(new SPNavigationNode(name, url));
                    }
                    else
                    {
                        nodeItem.Url = url;
                    }
                    nodeItem.Update();
                    nodeHeader.Update();
                }
                else
                {
                    //try to get node item under quicklaunch
                    SPNavigationNode nodeItem = quickLaunch.Parent.Children.Cast<SPNavigationNode>().Where(n => n.Title == name).FirstOrDefault();

                    //If item not found then create it
                    if (nodeItem == null)
                    {
                        nodeItem = quickLaunch.AddAsLast(new SPNavigationNode(name, url));
                    }
                    else
                    {
                        nodeItem.Url = url;
                    }
                    nodeItem.Update();
                }
            }
            else
            {
                if (header != null)
                {
                    SPNavigationNode nodeHeader = new SPNavigationNode(header, "");
                    quickLaunch.AddAsFirst(nodeHeader);
                    SPNavigationNode nodeItem = nodeHeader.Children.AddAsLast(new SPNavigationNode(name, url));
                    nodeItem.Update();
                    nodeHeader.Update();
                }
                else
                {
                    SPNavigationNode nodeItem = new SPNavigationNode(name, url);
                    quickLaunch.AddAsFirst(nodeItem);

                    nodeItem.Update();
                }
            }
        }
    }
}
  • FeatureActiveted is asynchronous so its possible the other feature's event receiver is not done yet when you clear the quick launch. For test in debug mode set a breakpoint before clearing the quick launch, give it a few seconds and then continue. – Naim Murati Feb 11 '15 at 22:49
  • You might be right. Other options I considered were that I was not properly updating the web object that SharePoint is building during site creation or that the addition of lists to the quicklaunch occurs later in the process. Lists in onet.xml would get added after features are activated so maybe there is a routine that adds all lists / libraries that have QuickLaunchOptions.On as a later step in the site creation ? I have struggled using attach to process to debug site creation. Maybe I need to attach to the central admin application pool ? Thanks for your suggestions Naim. – Satyrical Feb 12 '15 at 16:00
  • 1
    your thread.sleep proves my point. The other feature hasn't finished adding the lists when you cleared the quicklaunch. That's possible because the -ed events are asynchonous meaning they run in their own separate thread. About your trouble debagging the feature receive there are ways to solve that. One example here – Naim Murati Feb 12 '15 at 16:23
  • Thanks for that debugging link Naim, that is exactly what I needed. The reason that I am not 100% certain about the async issue is that I had the same problem with all of the code in a single feature, so I know the lists were created (and I assume added to the QL) BEFORE I cleared the QL and added fresh links. – Satyrical Feb 12 '15 at 16:41
1

I am still not sure 'why' links such as lists with 'QuickLaunchOptions.On' that are created before the last web feature is activated are either not being deleted or are being added after that feature completes. This only occurs when the feature is activated during site creation.

In any case the following code works to fix the problem. It causes the feature activated code to wait until the site collection has been created before executing.

Credit to Radu Tut for the following post which pointed me in the right direction - https://radutut.wordpress.com/2013/02/10/feature-stapling-and-site-provisioning-challenges/

using System.Threading;

public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        //Queues changes until after site exists.  For use in provisioning.
        SPWeb web = properties.Feature.Parent as SPWeb;
        ThreadPool.QueueUserWorkItem(ApplyYourChanges, web.Url);
    }

private void ApplyYourChanges(object state)
    {
        string webUrl = state as string;
        Uri uri = new Uri(webUrl);

        // additional conditions here -- perhaps check if a feature was activated
        while (!SPSite.Exists(uri))
        {
            Thread.Sleep(5000);
        }
        using (SPSite site = new SPSite(webUrl))
        {
            using (SPWeb web = site.OpenWeb())
            {
                //configure the quicklaunch menu
                configureQuickLaunch(web);
            }
        }
    }

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.