Saturday, April 19, 2014

Connect to publishing catalog programmatically in Sharepoint 2013

In this post I will describe the process of connecting to publishing catalog programmatically. As you probably know in Sharepoint a lot of actions can be done manually in UI. However if we develop solution with repeatable deployments it is more efficient to automate as much actions as possible. Almost all actions which you may do via UI can be done programmatically or by script in Sharepoint (in some cases you will need to use internal classes via Reflection, but it is still possible). And connecting to the publishing catalog is not exception from this point of view.

First of all before to go forward I recommend to check my previous articles about cross-site publishing and catalogs in Sharepoint 2013, especially Provision Sharepoint 2013 site collection with cross-site publishing. It will help to understand on which step during provisioning methods shown below should be called and other important moments:

Problem with cross site publishing catalog connections when use SPExport/SPImport for copying sites
Restore Sharepoint site with configured cross-site publishing on different environment
Cross site publishing and anonymous access in Sharepoint 2013
Problem with connecting to catalog on site collections imported with SPExport/SPImport API in Sharepoint

First of all we created Site scope feature for configuring search-related functionalities. This feature was activated with assumption that it can be activated and deactivated several times on the site collection. So for each step we have both configuration in FeatureActivated() method of feature receiver and deleting of configuration in FeatureDeactivating() method:

   1: public class SiteSearchEventReceiver : SPFeatureReceiver
   2: {
   3:     public override void FeatureActivated(SPFeatureReceiverProperties properties)
   4:     {
   5:         SPSite site = properties.Feature.Parent as SPSite;
   6:         SearchConfigurationHelper.ConnectAndConfigureNewsPublishingCatalog(
   7:             site.RootWeb, "My Catalog");
   8:         ...
   9:     }
  10:  
  11:     public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
  12:     {
  13:         SPSite site = properties.Feature.Parent as SPSite;
  14:         SearchConfigurationHelper.DeleteNewsPublishingCatalogConnection(site.RootWeb);
  15:         ...
  16:     }
  17: }

In SearchConfigurationHelper.ConnectAndConfigureNewsPublishingCatalog() we used slightly modified CatalogSubscriber from this MSDN example:

   1: public class CatalogSubscriber
   2: {
   3:     string portalSiteUrl;
   4:     string publishingCatalogUrl;
   5:     string subscribingWebServerRelativeUrl;
   6:     public CatalogSubscriber(string publishingCatalogUrl, string portalSiteUrl,
   7:         string subscribingWebServerRelativeUrl = "/")
   8:     {
   9:         if (string.IsNullOrWhiteSpace(portalSiteUrl) ||
  10:             string.IsNullOrWhiteSpace(publishingCatalogUrl))
  11:         {
  12:             throw new ArgumentNullException("A valid url must be provided for " +
  13:                 "publishingCatalogUrl and portalSiteUrl");
  14:         }
  15:         this.portalSiteUrl = portalSiteUrl;
  16:         this.publishingCatalogUrl = publishingCatalogUrl;
  17:         this.subscribingWebServerRelativeUrl = subscribingWebServerRelativeUrl;
  18:     }
  19:  
  20:     public void ConnectToCatalog()
  21:     {
  22:         using (SPSite publishingPortalSite = new SPSite(this.portalSiteUrl))
  23:         {
  24:             // Get the connection manager for the site where the catalog content
  25:             // will be rendered.
  26:             CatalogConnectionManager manager = new CatalogConnectionManager(
  27:                 publishingPortalSite, createResultSource: true);
  28:             // Use "Contains" to check whether a connection exists.
  29:             if (manager.Contains(this.publishingCatalogUrl))
  30:             {
  31:                 throw new ArgumentException(string.Format("A connection to {0} " +
  32:                     "already exists", this.publishingCatalogUrl));
  33:             }
  34:             // Get the catalog to connect to.
  35:             CatalogConnectionSettings settings =
  36:                 PublishingCatalogUtility.GetPublishingCatalog(publishingPortalSite,
  37:                     this.publishingCatalogUrl);
  38:             settings.ConnectedWebServerRelativeUrl =
  39:                 this.subscribingWebServerRelativeUrl;
  40:  
  41:             manager.AddCatalogConnection(settings);
  42:             // Perform any other adds/Updates
  43:             // ...
  44:             // Finally commit connection changes to store.
  45:             manager.Update();
  46:         }
  47:     }
  48:  
  49:     public void ConnectToCatalog(string[] orderedPropertiesForURLRewrite,
  50:         string taxonomyFieldManagedProperty)
  51:     {
  52:  
  53:         using (SPSite publishingPortalSite = new SPSite(this.portalSiteUrl))
  54:         {
  55:             // Get the connection manager for the site where the catalog
  56:             // content will be rendered.
  57:             CatalogConnectionManager manager =
  58:                 new CatalogConnectionManager(publishingPortalSite);
  59:             // Use "Contains" to check whether a connection exists.
  60:             if (manager.Contains(this.publishingCatalogUrl))
  61:             {
  62:                 throw new ArgumentException(string.Format("A connection to {0} " +
  63:                     "already exists", this.publishingCatalogUrl));
  64:             }
  65:             // Get the catalog to connect to.
  66:             CatalogConnectionSettings settings =
  67:                 PublishingCatalogUtility.GetPublishingCatalog(publishingPortalSite,
  68:                     this.publishingCatalogUrl);
  69:  
  70:             manager.AddCatalogConnection(settings,
  71:                 // If the items in the catalog should have rewritten urls, specify
  72:                 // what properties should be used in rewriting the url.
  73:                 orderedPropertiesForURLRewrite,
  74:                 this.subscribingWebServerRelativeUrl,
  75:                 // The managed property which contains the category for the catalog
  76:                 // item. Typically starts with owstaxid, when created by the system.
  77:                 taxonomyFieldManagedProperty);
  78:             // Perform any other adds/Updates
  79:             // ...
  80:             // Finally commit connection changes to store.
  81:             manager.Update();
  82:         }
  83:     }
  84:  
  85:     public void ConnectToCatalog(string newUrlTemplate,
  86:         string taxonomyFieldManagedProperty,
  87:         bool isCustomCatalogItemUrlRewriteTemplate,
  88:         string catalogName)
  89:     {
  90:         using (SPSite publishingPortalSite = new SPSite(this.portalSiteUrl))
  91:         {
  92:             // Get the connection manager for the site where the catalog
  93:             // content will be rendered.
  94:             CatalogConnectionManager manager =
  95:                 new CatalogConnectionManager(publishingPortalSite);
  96:             // Use "Contains" to check whether a connection exists.
  97:             if (manager.Contains(this.publishingCatalogUrl))
  98:             {
  99:                 throw new ArgumentException(string.Format("A connection to {0} " +
 100:                     "already exists", this.publishingCatalogUrl));
 101:             }
 102:             // Get the catalog to connect to.
 103:             //CatalogConnectionSettings settings =
 104:                 PublishingCatalogUtility.GetPublishingCatalog(publishingPortalSite,
 105:                     this.publishingCatalogUrl);
 106:             var settings = this.getCatalogByName(publishingPortalSite, catalogName);
 107:             if (settings == null)
 108:             {
 109:                 throw new Exception(string.Format("Can't connect to catalog '{0}': " +
 110:                     "it is not found", catalogName));
 111:             }
 112:  
 113:             manager.AddCatalogConnection(settings,
 114:                 // If the items in the catalog should have rewritten urls,
 115:                 // specify what properties should be used in rewriting the url.
 116:                 newUrlTemplate,
 117:                 this.subscribingWebServerRelativeUrl,
 118:                 // The managed property which contains the category for the catalog
 119:                 // item. Typically starts with owstaxid, when created by the system.
 120:                 taxonomyFieldManagedProperty,
 121:                 isCustomCatalogItemUrlRewriteTemplate);
 122:             // Perform any other adds/Updates
 123:             // ...
 124:             // Finally commit connection changes to store.
 125:             manager.Update();
 126:         }
 127:     }
 128:  
 129:     private CatalogConnectionSettings getCatalogByName(SPSite site,
 130:         string catalogName)
 131:     {
 132:         int num;
 133:         var catalogs = PublishingCatalogUtility.GetPublishingCatalogs(site, 0, 10,
 134:             string.Empty, out num);
 135:         if (catalogs == null)
 136:         {
 137:             return null;
 138:         }
 139:         foreach (var c in catalogs)
 140:         {
 141:             if (c.CatalogName == catalogName)
 142:             {
 143:                 return c;
 144:             }
 145:         }
 146:         return null;
 147:     }
 148:  
 149:     public void UpdateCatalogUrlTemplate(string newUrlTemplate)
 150:     {
 151:         using (SPSite publishingPortalSite = new SPSite(this.portalSiteUrl))
 152:         {
 153:             // Get the connection manager for the site where the catalog
 154:             // content will be rendered.
 155:             CatalogConnectionManager manager =
 156:                 new CatalogConnectionManager(publishingPortalSite);
 157:             // Get the current settings
 158:             CatalogConnectionSettings settings =
 159:                 manager.GetCatalogConnectionSettings(this.publishingCatalogUrl);
 160:             if (settings == null)
 161:             {
 162:                 throw new ArgumentException(
 163:                     string.Format("This site is not connected to catalog {0}",
 164:                         this.publishingCatalogUrl));
 165:             }
 166:             settings.CatalogItemUrlRewriteTemplate = newUrlTemplate;
 167:             // Update in memory
 168:             manager.UpdateCatalogConnection(settings);
 169:             // Perform any other adds/Updates
 170:  
 171:             // Finally Commit the update(s) to store.
 172:             manager.Update();
 173:         }
 174:     }
 175:  
 176:     public void DeleteCatalog()
 177:     {
 178:         using (SPSite publishingPortalSite = new SPSite(this.portalSiteUrl))
 179:         {
 180:             // Get the connection manager for the site where the catalog
 181:             // content will be rendered.
 182:             CatalogConnectionManager manager =
 183:                 new CatalogConnectionManager(publishingPortalSite);
 184:             // Use "Contains" to check whether a connection exists.
 185:             if (manager.Contains(this.publishingCatalogUrl))
 186:             {
 187:                 // Get the current settings
 188:                 CatalogConnectionSettings settings =
 189:                     manager.GetCatalogConnectionSettings(
 190:                         this.publishingCatalogUrl);
 191:  
 192:                 manager.DeleteCatalogConnection(this.publishingCatalogUrl);
 193:                 // Perform any other adds/Updates
 194:  
 195:                 // Finally Commit the update(s) to store.
 196:                 manager.Update();
 197:             }
 198:         }
 199:     }
 200: }

Here we used method getCatalogByName() (lines 126-147) which returns catalog from site collection by its name which work more stable than those which was used in MSDN example.

And in SearchConfigurationHelper.DeleteNewsPublishingCatalogConnection() method which is called in FeatureDeactivating() method we just called CatalogSubscriber.DeleteCatalog() method shown above (lines 176-197):

   1: public static void DeleteNewsPublishingCatalogConnection(SPWeb web)
   2: {
   3:     string newsCatalogUrl = ...;
   4:     var catalogSubscriber = new CatalogSubscriber(newsCatalogUrl, web.Url);
   5:     catalogSubscriber.DeleteCatalog();
   6: }

Hope that this information will help you if you will need to connect to publishing catalog programmatically.