Showing posts with label Content deployment. Show all posts
Showing posts with label Content deployment. Show all posts

Monday, April 7, 2014

Problem with cross site publishing catalog connections when use SPExport/SPImport for copying sites

In my blog I already wrote about various problems on sites after export/import (see e.g. Using SPExport/SPImport for copying content with managed metadata or Problem with SPContextPageInfo.IsWebWelcomePage on imported Sharepoint sites). There was also article about problems with connecting to catalogs on imported sites: Problem with connecting to catalog on site collections imported with SPExport/SPImport API in Sharepoint. In this post I will describe one more problem with publishing catalog connections on imported sites.

Suppose that we have site collection which uses cross-site publishing, i.e. has one or more connected catalogs. After that we export site collection via SPExport and import it to another location via SPImport. Now if we will check available catalog connections on the imported site in Site settings > Manage catalog connections there won’t be any connections. However if we will click Connect to catalog on the same page we will see all connections from the source site. The problem is that they still will point to the source site and it won’t be possible to disconnect them from UI (in the last column where normally there is Connect link, there will be server-relative URL of the source site collection which won’t be clickable).

In order to solve this problem we discussed the following approach with colleagues: if it is not possible to disconnect copied connections from UI, we will try to do it programmatically, and after that will check will it be possible to reconnect them from the target site. My colleague Mikko Niemi created console utility which enumerates all available connections and allows to disconnect them one by one. With his approval I post the code of this utility here:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         var siteUrl = args[0];
   6:  
   7:         Console.WriteLine("Trying to connect site '" + siteUrl + "'...");
   8:  
   9:         using (var site = new SPSite(siteUrl))
  10:         {
  11:             Console.ForegroundColor = ConsoleColor.Green;
  12:             Console.WriteLine("Connected to site '" + site.Url + "'");
  13:             Console.WriteLine();
  14:             Console.ForegroundColor = ConsoleColor.Gray;
  15:  
  16:             while (true)
  17:             {
  18:                 ShowAndDeleteConnections(site);
  19:  
  20:                 Console.WriteLine("To delete more press enter or " +
  21:                     "type q to quit:");
  22:                 var line = Console.ReadLine().ToLower();
  23:                 if (line == "q" || line == "quit")
  24:                 {
  25:                     break;
  26:                 }
  27:             }
  28:  
  29:         }
  30:  
  31:         Console.WriteLine();
  32:         Console.WriteLine("Finished, press key to close!");
  33:         Console.ReadKey();
  34:     }
  35:  
  36:     private static void ShowAndDeleteConnections(SPSite site)
  37:     {
  38:         Console.WriteLine("Fetching catalog connections... ");
  39:  
  40:         var catalogConnectionManager = new CatalogConnectionManager(site);
  41:         var catalogs = catalogConnectionManager.ConnectedPublishingCatalogs;
  42:  
  43:         if (catalogs == null || !catalogs.Any())
  44:         {
  45:             Console.ForegroundColor = ConsoleColor.Yellow;
  46:             Console.WriteLine("No connected catalog connections found!");
  47:             Console.ForegroundColor = ConsoleColor.Gray;
  48:         }
  49:         else
  50:         {
  51:             Console.WriteLine("Connected catalogs:");
  52:             var i = 1;
  53:             foreach (var c in catalogs)
  54:             {
  55:                 Console.ForegroundColor = ConsoleColor.White;
  56:                 Console.WriteLine("{0}. {1} ({2})", i, c.CatalogName,
  57:                     c.CatalogUrl);
  58:                 Console.WriteLine();
  59:                 Console.ForegroundColor = ConsoleColor.Gray;
  60:  
  61:                 i++;
  62:             }
  63:  
  64:             Console.WriteLine("Input the catalog number " +
  65:                 "that you want to delete (empty = no delete):");
  66:             var catalogIndex = Console.ReadLine();
  67:             if (!string.IsNullOrEmpty(catalogIndex))
  68:             {
  69:                 var catalogTodelete = catalogs[int.Parse(catalogIndex) - 1];
  70:                 Console.ForegroundColor = ConsoleColor.Yellow;
  71:                 Console.WriteLine("Deleting connection '" +
  72:                     catalogTodelete.CatalogName + "'...");
  73:                 Console.ForegroundColor = ConsoleColor.Gray;
  74:  
  75:                 catalogConnectionManager.
  76:                     DeleteCatalogConnection(catalogTodelete.CatalogUrl);
  77:                 catalogConnectionManager.Update();
  78:             }
  79:         }
  80:     }
  81: }
   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         var siteUrl = args[0];
   6:  
   7:         Console.WriteLine("Trying to connect site '" + siteUrl + "'...");
   8:  
   9:         using (var site = new SPSite(siteUrl))
  10:         {
  11:             Console.ForegroundColor = ConsoleColor.Green;
  12:             Console.WriteLine("Connected to site '" + site.Url + "'");
  13:             Console.WriteLine();
  14:             Console.ForegroundColor = ConsoleColor.Gray;
  15:  
  16:             while (true)
  17:             {
  18:                 ShowAndDeleteConnections(site);
  19:  
  20:                 Console.WriteLine("To delete more press enter or " +
  21:                     "type q to quit:");
  22:                 var line = Console.ReadLine().ToLower();
  23:                 if (line == "q" || line == "quit")
  24:                 {
  25:                     break;
  26:                 }
  27:             }
  28:  
  29:         }
  30:  
  31:         Console.WriteLine();
  32:         Console.WriteLine("Finished, press key to close!");
  33:         Console.ReadKey();
  34:     }
  35:  
  36:     private static void ShowAndDeleteConnections(SPSite site)
  37:     {
  38:         Console.WriteLine("Fetching catalog connections... ");
  39:  
  40:         var catalogConnectionManager = new CatalogConnectionManager(site);
  41:         var catalogs = catalogConnectionManager.ConnectedPublishingCatalogs;
  42:  
  43:         if (catalogs == null || !catalogs.Any())
  44:         {
  45:             Console.ForegroundColor = ConsoleColor.Yellow;
  46:             Console.WriteLine("No connected catalog connections found!");
  47:             Console.ForegroundColor = ConsoleColor.Gray;
  48:         }
  49:         else
  50:         {
  51:             Console.WriteLine("Connected catalogs:");
  52:             var i = 1;
  53:             foreach (var c in catalogs)
  54:             {
  55:                 Console.ForegroundColor = ConsoleColor.White;
  56:                 Console.WriteLine("{0}. {1} ({2})", i, c.CatalogName,
  57:                     c.CatalogUrl);
  58:                 Console.WriteLine();
  59:                 Console.ForegroundColor = ConsoleColor.Gray;
  60:  
  61:                 i++;
  62:             }
  63:  
  64:             Console.WriteLine("Input the catalog number " +
  65:                 "that you want to delete (empty = no delete):");
  66:             var catalogIndex = Console.ReadLine();
  67:             if (!string.IsNullOrEmpty(catalogIndex))
  68:             {
  69:                 var catalogTodelete = catalogs[int.Parse(catalogIndex) - 1];
  70:                 Console.ForegroundColor = ConsoleColor.Yellow;
  71:                 Console.WriteLine("Deleting connection '" +
  72:                     catalogTodelete.CatalogName + "'...");
  73:                 Console.ForegroundColor = ConsoleColor.Gray;
  74:  
  75:                 catalogConnectionManager.DeleteCatalogConnection(
  76:                     catalogTodelete.CatalogUrl);
  77:                 catalogConnectionManager.Update();
  78:             }
  79:         }
  80:     }
  81: }

With this utility you may disconnect copied “broken” connections and reconnect them again e.g. from UI (after disconnecting near appropriate connection you will see Connect link). Hope that it will help you if you will face with similar problem.

Wednesday, December 4, 2013

Problem with connecting to catalog on site collections imported with SPExport/SPImport API in Sharepoint

Some time ago I wrote about problem with TaxonomyHiddenList on site collections which were created using SPExport/SPImport API (see Fix TaxonomyHiddenList guid after export/import of site collections in Sharepoint). In this post I will tell about one more problem on imported site collection and will show how to fix it.

When you try to connect to catalog on this site collection (Site settings > Manage catalog connections) you will get the following error:

The specified view is invalid

The error comes from CatalogSource.aspx page, more specifically from its codebehind method Microsoft.SharePoint.Publishing.Internal.CodeBehind.CatalogSourcePage.InitializeControlsForAdd() when it tries to populate list of masterpages:

   1:  private void InitializeControlsForAdd()
   2:  {
   3:      ...
   4:      List<DropDownListWithDetails.ItemInfo> masterPagesDataList =
   5:  DesignUtilities.GetMasterPagesDataList(base.Site, false);
   6:      this.rollupMasterPageSelectionControl.DataList = masterPagesDataList.ToArray();
   7:      this.viewerMasterPageSelectionControl.DataList = masterPagesDataList.ToArray();
   8:  }

Method DesignUtilities.GetMasterPagesDataList() calls other method GetHtmlMasterPages() which causes the mentioned error:

   1:  internal static SPListItemCollection GetHtmlMasterPages(SPWeb web)
   2:  {
   3:      SPList catalog = web.GetCatalog(SPListTemplateType.MasterPageCatalog);
   4:      Guid guid = new Guid(web.Properties["HtmlDesignViewNameHtmlMasterPages"]);
   5:      SPView view = catalog.Views[guid];
   6:      SPQuery query = new SPQuery(view);
   7:      query.RowLimit = 0;
   8:      return catalog.GetItems(query);
   9:  }

I.e. after import web property bag value remain the same, while object identities are changed if you don’t use RetainObjectIdentity = true. As you can see from the code above in the HtmlDesignViewNameHtmlMasterPages property in the property bag of the root site of site collection, list view id is stored for /_catalogs/masterpage list. The question is what exactly view should be used? Analysis showed that it is view with title “Html Master Pages”. So in order to fix it you may use the following script:

   1:  param( 
   2:      [string]$url
   3:  )
   4:   
   5:  function Fix-Html-Master-Pages-View($w)
   6:  {
   7:      $pv = $w.Properties["HtmlDesignViewNameHtmlMasterPages"]
   8:      $c = $w.GetCatalog([Microsoft.SharePoint.SPListTemplateType]::MasterPageCatalog);
   9:      $id = $c.Views["Html Master Pages"].ID
  10:      Write-Host "Property bag value: '$pv', view id: '$id'"
  11:      if ($pv.ToString().ToLower() -ne $id.ToString().ToLower())
  12:      {
  13:          Write-Host "Property bag value differs from view id. It will be fixed" -foregroundcolor yellow
  14:          $w.Properties["HtmlDesignViewNameHtmlMasterPages"] = $id
  15:          $w.Properties.Update()
  16:          Write-Host "Property bag value was successfully fixed" -foregroundcolor green
  17:      }
  18:      else
  19:      {
  20:          Write-Host "Property bag value equals to view id. Nothing should be fixed" -foregroundcolor green
  21:      }
  22:  }
  23:   
  24:  if (-not $url)
  25:  {
  26:      Write-Host "Specify web app url in url parameter" -foregroundcolor red
  27:      return
  28:  }
  29:   
  30:  $wa = Get-SPWebApplication $url
  31:  foreach ($s in $wa.Sites)
  32:  {
  33:      Write-Host "Checking $s.Url..."
  34:      Fix-Html-Master-Pages-View $s.RootWeb
  35:  }

It will enumerate through all site collections and will fix the property bag value to the correct view id which corresponds to the current site collection.

Thursday, November 28, 2013

Fix TaxonomyHiddenList guid after export/import of site collections in Sharepoint

In this post I will describe one more problem with using SPExport/SPImport API (see previous similar posts here, here and here). As you probably know TaxonomyHiddenList is special hidden list used by Sharepoint for using managed metadata on your site. It is located in the root site of site collection. One thing which is not so well known is that id of this list is also stored in the property bag of the site collection’s root web (SPWeb.Properties) in “TaxonomyHiddenList” key. The problem is that if you export existing site collection with SPExport API and then import it with SPImport with SPImportSettings.RetainObjectIdentity = false (which means that all objects in imported site will have new guids), list id is changed while value in property bag remains the same from source site collection. It causes many problems with managed metadata on exported site (e.g. it is not possible to save values in managed metadata fields).

In order to fix TaxonomyHiddenList value in the property bag I created the following PowerShell script:

   1:  function Fix-Taxonomy-Hidden-List($w)
   2:  {
   3:      $pv = $w.Properties["TaxonomyHiddenList"]
   4:      $l = $w.Lists["TaxonomyHiddenList"]
   5:      $id = $l.ID
   6:      Write-Host "    Property bag value: '$pv', list id: '$id'"
   7:      if ($pv.ToString().ToLower() -ne $id.ToString().ToLower())
   8:      {
   9:          Write-Host "    Property bag value differs from list id. It will be fixed" -foregroundcolor yellow
  10:          $w.Properties["TaxonomyHiddenList"] = $id
  11:          $w.Properties.Update()
  12:          Write-Host "    Property bag value was sucessfully fixed" -foregroundcolor green
  13:      }
  14:      else
  15:      {
  16:          Write-Host "    Property bag value equals to list id. Nothing should be fixed" -foregroundcolor green
  17:      }
  18:  }
  19:   
  20:  $wa = Get-SPWebApplication "http://example.com"
  21:  foreach ($s in $wa.Sites)
  22:  {
  23:      Write-Host "Checking $s.Url..."
  24:      Fix-Taxonomy-Hidden-List $s.RootWeb
  25:  }

It enumerates all site collections in specified web application (lines 21-25), checks value for “TaxonomyHiddenList” key in web property bag, compares it with TaxonomyHiddenList list guid and if they are different, it stores new value into property bag. You may run this script several times: after first time it will see that value in property bag equals the list id and won’t do anything. In order to use it on your environment change web application url on line 20 on your own. Hope it will help someone.