Friday, March 11, 2016

Change content of master page in Sharepoint programmatically

There are many articles available of how you may set master page for the site (using SPWeb.MasterUrl and SPWeb.CustomMasterUrl), but as it turned out there is a lack of examples which show how you may programmatically change content of master page itself. Sometimes it may be needed, e.g. when there is a lot of site collections in web application and updating them manually would take too much time.

The following example shows how to modify content of master page via Sharepoint object model:

   1:  
   2: var web = site.RootWeb;
   3: var file = web.GetFile(web.MasterUrl);
   4: if (file.CheckOutStatus != SPFile.SPCheckOutStatus.None)
   5: {
   6:     file.UndoCheckOut();
   7: }
   8:  
   9: string content = "";
  10: using (var sr = new StreamReader(file.OpenBinaryStream()))
  11: {
  12:     content = sr.ReadToEnd();
  13: }
  14:  
  15: string insertAfter = "</SharePoint:SPSecurityTrimmedControl>";
  16: int idx = content.IndexOf(insertAfter);
  17: if (idx < 0)
  18: {
  19:     return;
  20: }
  21:  
  22: int insertAt = idx + insertAfter.Length;
  23:  
  24: var subStr1 = content.Substring(0, insertAt);
  25: var subStr2 = content.Substring(insertAt);
  26: string newStr =
  27:     subStr1 +
  28:     "<div>New content</div>" +
  29:     subStr2;
  30:  
  31: file.CheckOut();
  32:  
  33: var catalog = site.GetCatalog(SPListTemplateType.MasterPageCatalog);
  34: var files = catalog.RootFolder.Files;
  35: file = files.Add(web.MasterUrl, Encoding.UTF8.GetBytes(newStr), true);
  36:  
  37: file.CheckIn("Change masterpage programmatically");
  38:  
  39: if (file.Item.ParentList.EnableMinorVersions)
  40: {
  41:     file.Publish("Change masterpage programmatically");
  42: }
  43:  
  44: if (file.Item.ParentList.EnableModeration)
  45: {
  46:     file.Approve("Change masterpage programmatically");
  47: }

In this example we insert div element with custom content after first found SPSecurityTrimmedControl. In your scenarios you may need to insert another elements into another places in masterpage. In this case modify the logic as needed.

At first we read the content of the masterpage and store it to the string (lines 9-13). After that we determine place where new element should be inserted (lines 15-22). In next step we split content string into 2 sub strings: before and after insert position (lines 24-25). And finally insert new element to the string (lines 26-29). We could perform the same operations via regular expressions, but in this example we will keep it simple. After that we override existing file with the new content (lines 33-35) and check in, publish, approve it depending on masterpage gallery’s settings (lines 37-47).

In result we will have updated masterpage which content was changed programmatically. Hope that this information will help someone.

Change search settings via client object model on SPWeb level in Sharepoint

In Sharepoint you may set search settings for whole site collection (Site settings > Site collection administration > Search settings) or for particular site (Site settings > Search > Search settings):

In search settings page you may change such settings as search center url, search results page, search navigation (tabs which are displayed on search results page):

Settings on SPWeb level have priority over site collection settings. When it may be useful to use search settings on SPWeb level? Consider the following example: we have single site collection and number of language sub sites in it:

For better user experience we may create own search center for each language version as sub site of particular language site: http://example.com/en/search and http://example.com/fi/haku (in this example we also use localized urls). In this case search requests from English site should go to English search center, and from Finnish – to Finnish one. How we can set search settings for particular SPWeb?

The following code shows how we may do it via client object model:

   1: var clientContext = new ClientContext("http://example.com/en");
   2: var secure = new SecureString();
   3: foreach (char c in "password")
   4: {
   5:     secure.AppendChar(c);
   6: }
   7: var credentials = new SharePointOnlineCredentials("user@example.com", secure);
   8: clientContext.Credentials = credentials;
   9: var web = clientContext.Web;
  10:  
  11: clientContext.Load(web, w => w.AllProperties);
  12: clientContext.ExecuteQuery();
  13:  
  14: var allProperties = web.AllProperties;
  15: allProperties["SRCH_ENH_FTR_URL_WEB"] = "~sitecollection/en/search/pages";
  16: allProperties["SRCH_SB_SET_WEB"] = "{\"Inherit\":false,\"ResultsPageAddress\":" +
  17:     "\"~sitecollection/en/search/pages/results.aspx\",\"ShowNavigation\":false}";
  18:  
  19: web.Update();
  20: clientContext.ExecuteQuery();

For Finnish sub site code will be the same, except site url and urls of search results page specified in SRCH_ENH_FTR_URL_WEB and SRCH_SB_SET_WEB property bag settings. Also let’s mention that settings on site collection level can be set by similar way with another property bag settings: SRCH_ENH_FTR_URL_SITE and SRCH_SB_SET_SITE (see e.g. this example of how to use them: Set search settings in all site collections of Sharepoint web application via PowerShell).

As we use client object model this approach can be used for setting search settings for Sharepoint Online as well.

Thursday, March 3, 2016

Problem with broken layout in Sharepoint Online when use themes

During provisioning of big sites which uses themes into Sharepoint online via client object model you may face with the following problem: layout may be broken although there were no issues during provisioning. Symptoms may be the following:

I.e. in top left corner there will be following links visible which are hidden in regular state:

  • Turn on more accessible mode
  • Skip to main content
  • Turn off animations

If you face with similar issue check 4 following files in /_catalogs/theme/themed/{id} folder (id will be unique for each installation):

  • COREV15-51C31438.themedcss
  • COREV15-88C811FA.themedcss
  • corev15app-DDE41C8D.themedcss
  • corev15app-EA1D5047.themedcss

Their normal size is about 250Kb. If they have less size something could go wrong during provisioning and they may contain incorrect css which breaks layout of the site:

   1:  
   2: /* _lcid="1035" _version="16.0.5004"
   3: _LocalBinding */
   4: body,
   5: .ms-core-defaultFont,
   6: #pageStatusBar,
   7: .ms-status-msg,
   8: .js-callout-body
   9: {
  10:  font-family:"Segoe UI","Segoe",Tahoma,Helvetica,Arial,sans-serif;
  11: font-size:13px;
  12: }
  13: body,
  14: .ms-core-defaultFont,
  15: .js-callout-body
  16: {
  17:  color:#294754;
  18: }
  19: .ms-core-defaultFont
  20: {
  21: font-weight:normal;
  22: text-decoration:none;
  23: white-space:normal;
  24: word-break:normal;
  25: line-height:normal;
  26: }
  27: body
  28: {
  29: margin:0px;
  30: overflow:hidden;
  31:  background-color:#fff;
  32: background-size:cover;
  33: background-repeat:no-repeat;
  34: }
  35: html > .ms-core-needIEFilter
  36: {
  37:  -ms-filter:

In order to fix this issue copy content of these files from working sites and replace incorrect css. After that layout will be fixed.