I've moved my blog to!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Friday, September 21, 2007

Update V2 to V3 Area Url Mappings

So I've been moving my web sites all around, completely changing the hierarchy, creating new site collections, etc., and I discovered that whenever I hit a link that still pointed to an old bucket web (http://intranet/c16/ebusiness instead of http://intranet/topics/divisions/ebusiness) it loaded up a page called spsredirect.aspx which was supposed to redirect me to the new, correct link.

Unfortunately because I've moved things around the page was returning a 404. After disassembling the spsredirect page (Microsoft.SharePoint.Portal.WebControls.SPSRedirectPage) I found that what is happening is that the page is looking at a special hard-coded list that gets created when you are doing an upgrade. The list has a fixed name: "98d3057cd9024c27b2007643c1". Joel Oleson has a brief article which touches upon this list: The list contains the old bucket web links and the new links. What I found was that if I updated the V3ServerRelativeUrl field I could now move my webs around (within the same web application - more on that in a bit) and then simply update the corresponding field entry in this list and now any old bucket web links will still work.

What's irritating is that the spsredirect.aspx page expects all the URLs to be server relative so if you're moving a web to a new web app then you're out of luck (unless you create your own custom redirect page and then set the links to use that page and pass in the actual URL - that way spsredirect will redirect to your custom redirect page which will then redirect to the actual page - I decided to just limit my command to working with a single web app rather than try to deal with this scenario).

Of course changing the entries within here doesn't actually fix the links, but it will enable you to move things around and have the old v2 links still redirect correctly. I've created this new command to handle changing the new V3 URL and I've also modified my gl-moveweb and gl-convertsubsitetositecollection commands to utilize this functionality as well so if you run either of those two commands it will attempt to fix the links in this list if it can. The core code to do all this is detailed below:

   1: /// <summary>
   2: /// Fixes the upgraded area URL mappings.  This overload is used when moving a web within a single site collection.
   3: /// </summary>
   4: /// <param name="site">The site.</param>
   5: /// <param name="sourceWebUrl">The source web URL.</param>
   6: /// <param name="targetWebUrl">The target web URL.</param>
   7: private static void FixUpgradeAreaUrlMappings(SPSite site, string sourceWebUrl, string targetWebUrl)
   8: {
   9:  SPList list = GetUpgradeAreaUrlMappingsList(site);
  11:  if (list == null)
  12:   return;
  14:  FixUpgradeAreaUrlMappings(list, sourceWebUrl, targetWebUrl);
  15: }
  17: /// <summary>
  18: /// Gets the upgrade area URL mappings list.
  19: /// </summary>
  20: /// <param name="site">The site.</param>
  21: /// <returns></returns>
  22: internal static SPList GetUpgradeAreaUrlMappingsList(SPSite site)
  23: {
  24:  try
  25:  {
  26:   // 98d3057cd9024c27b2007643c1 is a special hard coded name for a list that Microsoft uses to store the mapping
  27:   // of URLs from v2 to v3 (maps the bucket urls to the new urls).
  28:   return Utilities.GetListByUrl(site.RootWeb, "98d3057cd9024c27b2007643c1", false);
  29:  }
  30:  catch (Exception)
  31:  {
  32:   return null;
  33:  }
  35: }
  36: /// <summary>
  37: /// Fixes the upgraded Area URL mappings.
  38: /// </summary>
  39: /// <param name="list">The list.</param>
  40: /// <param name="sourceWebUrl">The source web URL.</param>
  41: /// <param name="targetWebUrl">The target web URL.</param>
  42: private static void FixUpgradeAreaUrlMappings(SPList list, string sourceWebUrl, string targetWebUrl)
  43: {
  44:  targetWebUrl = targetWebUrl.TrimEnd('/');
  45:  sourceWebUrl = sourceWebUrl.TrimEnd('/');
  47:  using (SPSite rootSite = list.ParentWeb.Site)
  48:  {
  49:   foreach (SPListItem item in list.Items)
  50:   {
  51:    string v3Url = item["V3ServerRelativeUrl"] as string;
  52:    if (string.IsNullOrEmpty(v3Url))
  53:     continue;
  55:    if (v3Url.ToLowerInvariant().StartsWith(sourceWebUrl.ToLowerInvariant()))
  56:    {
  57:     v3Url = targetWebUrl + v3Url.Substring(sourceWebUrl.Length);
  58:     item["V3ServerRelativeUrl"] = v3Url;
  61:     SPSite targetSite;
  62:     try
  63:     {
  64:      targetSite = new SPSite(rootSite.MakeFullUrl(v3Url));
  65:     }
  66:     catch (Exception)
  67:     {
  68:      targetSite = null;
  69:     }
  70:     if (targetSite != null)
  71:     {
  72:      using (SPWeb targetWeb = targetSite.AllWebs[v3Url])
  73:      {
  74:       if (targetWeb.Exists)
  75:       {
  76:        item["V3WebId"] = targetWeb.ID.ToString();
  77:       }
  78:      }
  79:     }
  81:     item.Update();
  82:    }
  83:   }
  84:  }
  85: }

The command I created is detailed below (the name is really verbose but I couldn't think of anything shorter that would be clear as to it's intentions).

The command, gl-updatev2tov3upgradeareaurlmappings, is very straightforward - it expects the web application that the upgrade list resides on and the source and target URLs. Keep in mind that these URLsthough can be specified as absolute they will be converted to server relative and they must correspond to webs on the specified web application (note that I made the code sensitive enough so that you could specify a non-existent source or target in the event that you have already moved your site or are about to move it - it's always better though if the target actually exists so that it can set the web ID properly).

The syntax of the command can be seen below:

C:\>stsadm -help gl-updatev2tov3upgradeareaurlmappings

stsadm -o gl-updatev2tov3upgradeareaurlmappings

Updates the server relative URL corresponding to the source URL to reflect the new target URL in the Upgrade Area URL Mappings list: "http://portal/Lists/98d3057cd9024c27b2007643c1/AllItems.aspx".  All sites below the site are also updated.

        -webapp <web application>
        -sourceurl <source V3 url (this is not the V2 bucket URL)>
        -targeturl <target V3 URL>

Here’s an example of how to change the redirect URL for a given web:

stsadm –o gl-updatev2tov3upgradeareaurlmappings -webapp "http://intranet/" -sourceurl "/topics/divisions/humanresources" -targeturl "/hr"

1 comment:

Anu said...


Thanks for developing this using tool.but i have problem in implementing "gl-updatev2tov3upgradeareaurlmappings".
From your blog i understand that it reads from "Upgrade Area URL Mapping" list.I have made enteries in the list as follows:
V3Relativeurl -"/Sites/Area1"

The idea behind this is after migration(Database Migration) from SPS 2003 to MOSS ,the bucket webs was not converted to Site Collection as "Sites/".I read in many Migration Articles that the urls of bucket webs will get converted automatically to MOSS Structure.
After reading your post i was able to find that "Secret List" and made enteries
and executed your command
"stsadm -o gl-updatev2tov3upgradeareaurlmappings -webapp "http://servername" -sourceurl "/C19/Area1" -targeturl "/Sites/Finance".
It says operation completes successfully but
NO redirection happened
I also noticed that for Source url you mentioned not to use bucket webs,then How will i do redirection if i have bucket webs

Thanks a lots