1

I want to delete some of the webparts from my sites when they are created. I've stapled a feature that runs custom code. I've attached the basics of what it does below, edited for brevity.

It works but when you create a my site you get the error message "The operation could not be completed because the Web Part was deleted by another user or is invalid.", I'd guess because it's trying to load the webparts that the background thread is halfway through deleting. Refresh and the page loads fine.

Any ideas how to get around this?

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    thread.Start(deletethestuff)        
}

public override void DeleteTheStuff
{
    while (checksiteisprovisioned == false) Thread.Sleep(1000);

    SPFile page = web.RootFolder.Files["default.aspx"];
    SPLimitedWebPartManager manager = page.GetLimitedWebPartManager(PersonalizationScope.Shared));
    foreach (Microsoft.SharePoint.WebPartPages.WebPart webPart in webParts)
    {
        if (webPart.Title == "onetodelete")
        {
            manager.DeleteWebPart(webPart);
        }
    }
    page.Update();
}

4 Answers 4

1

The way i do this is as described in Steve Peschka's blog post on the SharePoint team blog: http://blogs.msdn.com/sharepoint/archive/2007/03/22/customizing-moss-2007-my-sites-within-the-enterprise.aspx

1
  • bob on. adding a custom control in the masterpage which edits and then refreshes the page worked a charm.
    – Anonymous
    Commented Apr 29, 2010 at 10:45
3

The issue here is that you are modifying the web parts collection at the same time as iterating through it. You need to build a separate collection and then use that for the deletion e.g.:

SPFile page = web.RootFolder.Files["default.aspx"];
SPLimitedWebPartManager wpmShared = page.GetLimitedWebPartManager(PersonalizationScope.Shared);

List<WebPart> partsToProcess = new List<WebPart>();

foreach (WebPart part in wpmShared.WebParts)
{
     if (part.Title == "onetodelete")
     {
         partsToProcess.Add(part);
     }
}

foreach (WebPart webPart in partsToProcess)
{
     wpmShared.DeleteWebPart(webPart);
}     

page.Update();
2
  • i've actually already got that in my code but trimmed it out. sorry it wasn't clear.
    – Anonymous
    Commented Apr 29, 2010 at 8:26
  • Then your code should be fine. I'm using this exact pattern myself in the framework Steve Peschka describes in the article on the SharePoint blog (referenced by Anders below). Also, you are running under elevated privileges yep? Commented Apr 29, 2010 at 8:58
1

The way i've always done this is to create another feature that would delete the webparts but not staple it to any sites. Then I would have another feature that is stapled to the sites that would kick off two things:

One, I used threads and it works pretty well but I did it slightly different than you:

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    using (SPWeb web = (SPWeb)properties.Feature.Parent)
    {
        if (web == null) throw new ApplicationException("Web could not be found");

        ThreadPool.QueueUserWorkItem(new WaitCallback(RunProcess), web.Url);
    }
}

private void RunProcess(object state)
{
    bool provisioned = false;
    SPWeb web = null;

    try
    {
        for (int i = 0; i < 100 && provisioned == false; i++)
        {
            try
            {
                string url = (string)state;
                using (SPSite site = new SPSite(url))
                {
                    web = site.OpenWeb();
                    if (web.Provisioned)
                    {
                        provisioned = true;
                        break;
                    }

                    if (web != null)
                    {
                        web.Dispose();
                    }

                    Thread.Sleep(3000);
                }
            }
            catch { }
            finally
            {
                if (web != null &&
                    provisioned == false)
                {
                    web.Dispose();
                }
            }
        }

        if (provisioned)
        {
            DoSomething(web);
        }
        else
        {
            throw new ApplicationException("Web site never provisioned");
        }
    }
    finally
    {
        if (web != null)
        {
            web.Dispose();
        }
    }
}

and then in do something I would activate the unstapled feature.

Two. I would add a one time timer job set to run in 5 minutes that would also activate the same feature.

This way 99% of the time the thread would execute correctly, but the 1% that is doesn't, there is a timer job that will take care of it after a little bit of a delay.

2
  • I guess I really didn't asnwer it completely. Your issue is definitly what Chris states. But to get around the issue Wickor pointed out, check out my code above. Commented Apr 28, 2010 at 17:33
  • This is the correct way to use threads if you are going to do it, ie use the thread pool. Commented Apr 28, 2010 at 17:48
0

I would never go with a background thread like this during the site provisioning, it is just made to fail.

In your case I would create a copy of the MSITE Site definition, without the Web Parts and use that site def for the My Sites instead.

Your Answer

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