Wednesday, December 29, 2010

Preparation for MOSS Upgrade to SharePoint 2010 - Part 1

In order to prepare for a major upgrade from MOSS 2007/WSS 3 to SharePoint 2010/SPF 4, one of the steps involved was to identify "unghosted objects" in the current Farm.  After some analysis, that list narrows down to the following types:
  • Masterpage and page layouts for Publishing Portals
  • View Forms
  • List Forms
  • Root Level files
The outer code to iterate through objects in the Farm will be something like that shown below:

try

{
//Get all Root Web Applications
SPWebApplication webApp = SPWebApplication.Lookup(new Uri(webappURL));

//Get all Sitecollections in farm
foreach (SPSite opsite in webApp.Sites)
{
try
{
foreach (SPWeb web in opsite.AllWebs)
{
try
{
GetUnghostedForms(webApp, opsite, web);
GetUnghostedFilesfromLibraries (webApp, opsite, web);
}
catch (Exception ex)
{
errors += "\n\nWEB ERRORS: " + ex.ToString();
continue;
}

finally
{
if (web != null)
web.Dispose();
}
}
//Console.WriteLine("DEBUG: site" + site.ToString());
}
catch (Exception ex)
{
errors += "\n\nSITE ERRORS: " + ex.ToString();
continue;
}


finally

{

if (opsite != null)

opsite.Dispose();

}

}

}



catch (Exception ex)

{

errors += "\n\nWEBAPP ERRORS: " + ex.ToString();
}

The methods shown above expand as follows:

static void GetUnghostedFilesfromLibraries(SPWebApplication webApp, SPSite site, SPWeb web)

{
string errors = string.Empty;

string printout = string.Empty;



#region Pages and masterpage Library


try

{

SPList masterList = web.Lists["Master Page Gallery"];

SPQuery oQuery = new SPQuery();

oQuery.Query = string.Format(".aspx.master");

SPListItemCollection colmasterListItems = masterList.GetItems(oQuery);



foreach (SPListItem currentItem in colmasterListItems)

{

try

{

SPFile file = web.GetFile(currentItem.File.Url);

if (file.CustomizedPageStatus == SPCustomizedPageStatus.Customized)

{


//add code to collect data...


}

}

catch (Exception ex)

{

errors += "\n\nPAGE ERRORS: " + ex.ToString();

continue;

}

}

}

catch (Exception ex)

{
errors += "\n\nPAGE ERRORS: " + ex.ToString();
}
#endregion
}

static void GetUnghostedForms( SPWebApplication webApp, SPSite site, SPWeb web) 
{
string errors = string.Empty;
string printout = string.Empty;


try
{

#region Root Level Files

SPFileCollection filesList = web.Files;


foreach (SPFile ifile in filesList)
{
try
{
if (ifile.CustomizedPageStatus == SPCustomizedPageStatus.Customized)
{
//code to collect data


}
}
catch (Exception ex)
{
errors += "\n\nPAGE ERRORS: " + ex.ToString();
continue;
}
}

#endregion

foreach (SPList listForms in web.Lists)
{
SPFormCollection collForms = listForms.Forms;
SPViewCollection collViews = listForms.Views;

#region List Forms

foreach (SPForm oForm in collForms)
{
try
{
if (oForm.Url.EndsWith(".aspx"))
{
SPFile file = null;
file = web.GetFile(web.Url + "/" + oForm.Url);
if (file.CustomizedPageStatus == SPCustomizedPageStatus.Customized)
{
//code to collect data


}
}
}
catch (Exception ex)
{
errors += "\n\nFORM ERRORS: " + ex.ToString();
continue;
}
}

#endregion


#region View Forms

foreach (SPView oView in collViews)
{
//exclude hidden views


if (oView.Hidden == false)
{
try
{
string url = oView.Url;


if (oView.Url.EndsWith(".aspx"))
{
SPFile file = null;
file = web.GetFile(web.Url + "/" + oView.Url);


if (file.CustomizedPageStatus == SPCustomizedPageStatus.Customized)
{


//code to collect data


}
}
}
catch (Exception ex)
{
errors += "\n\nVIEW ERRORS: " + ex.ToString();
continue;
}}
}

#endregion}
}
catch (Exception ex)
{
errors += "\n\nWEB ERRORS: " + ex.ToString();
}
}

Unghosted SharePoint objects pose an unusual challenge to the upgrade process in the sense that any default or out of the box items may replace customized ones. The end users almost always are upset when the functionality is missing and may tend to start believing that the upgrade was done poorly and their "work was lost". It will be upto the Department Managers to inform users that MOSS upgrades will require some fine tuning to maintain and restore full functionality.


So now that we have identified these customized or unghosted objects, what do we do next? This will determine on the organization structure. If you have thousands of Site Collections as we did, the responsibility will trickle down to the SCA's (Primary and Secondary Site Collection Admins). The organization will have to determine a global strategy. While code may be written to reset the definitions back to the default, in other cases, it may be necessary to recreate some of these pages once the upgrade is completed.

Wednesday, December 22, 2010

The object has been updated by another user since it was last fetched" when programmatically updating a child content type

We ran into the same issue and spent hours trying to resolve the error, and finally found a solution so I thought it would be helpful to someone else.

The scenario is a little different and actually simpler that what's described above by Djamel Chagour. We are adding a SPFieldLink to an existing SPContentType, the document library content type. We require a special column to be added to all document libraries and children of document libaries. Earlier we had tried the standard code snippet as shown below:

SPFieldLink fieldlink = new SPFieldLink(field);

libContentType.FieldLinks.Add(fieldlink);

libContentType.Update(true, false);

thisweb.Update();

- surrounded within using statements for SPweb and SPSite ofcourse. This was giving the error:

Microsoft.SharePoint.SPException: The object has been updated by another user since it was last fetched. at Microsoft.SharePoint.SPContentType.UpdateOnWeb(Boolean bPushdown, Boolean bThrowOnSealedOrReadOnly) at Microsoft.SharePoint.SPContentType.Update(Boolean updateChildren, Boolean throwOnSealedOrReadOnly) at EnterpriseTaxonomySharePointFeature.FeatureReceiver.FeatureActivated(SPFeatureReceiverProperties properties)

We changed the code to the following:

SPFieldLink fieldlink = new SPFieldLink(field);
libContentType.FieldLinks.Add(fieldlink);


//update parent


libContentType.Update(false, false);
thisweb.Update();
UpdateChildren(osite, libContentType);






private void UpdateChildren(SPSite site, SPContentType type)
{
using (SPWeb web = site.RootWeb)
{


//update children


Type.Update(true, false);
web.Update();
}
}

This prevents the SPException error. Hope this helps anyone else looking for a soultion.

Saturday, September 4, 2010

SharePoint Online Services via BPOS

BP's Oil Spill Response included two key data collection systems.  One was called Vessels of Opportunity (VOO) and the other Vessel Demobilization System (VDS).  This post is about the innovative use of SharePoint Online Services for both these systems.  The business requirement was to create site collections for each of the branches of BP's response centers for access by Admins, Ship Owners, key Business Stakeholders, including the US Coast Guard and BP.  So what were the key requirements for these systems?  Here's a short list:
  • Fast development and deployment process
  • Unified security and easy access from anywhere in the world
  • Simple to implement with relatively low cost
  • Inherently well oranized and structured pages with proper taxonomy
  • Easy to extend across other departments, divisions or agencies
Guess what technology meets these needs: SharePoint Online Services via the Business Productivity Suite or BPOS.  Using BPOS, the Site Admins were able to create Users as requested, and the SharePoint Developers (including myself) were able to respond to the business needs within hours of the request.  We created several "department dashboards" that allowed Users to see their department status in one area.  The Site Permissions took care of the security and access levels. 

So if these were the pros of this technology, you ask what the cons were?  The downside was that Developers had no access to the Servers, or in effect there was no straight forward ability to run stsadm commands.  Here's a summary of features NOT Available in Microsoft BPOS:
  1. Anonymous users supported
  2. UI enhancements
  3. Server control
  4. Customization / Code Deployment
  5. 3rd party web parts installation
  6. Unlimited user account creation with no additional fees
  7. Daily site backups
  8. Custom domain names supported
  9. Dedicated solution for any number of users
The other interesting aspect of this project was that there was a 'consultant circus' with so many IT Consulting firms all involved with parts of the same project, from Infosys to Microsoft Consulting, to Neudesic, to ComSys, Deloitte and private contractors. However, it all worked out well on these projects at Houston, TX, Houma, LA and Mobile, AL. 
One of the technical challenges came about when one of the business requirements for the VDS system was custom workflows, custom access levels and real time data charting.  That's where SharePoint Online Services began to appear as not fitting the bill.  So we migrated the technology to Windows Azure, writing a custom .NET application hosted in the Cloud.  It was a classic example of where .NET still rules, when customization and granular access outweigh simplicity, ease of use and fast deployment times.