Tuesday, March 18, 2014

Group SharePoint site templates within a site definition using configuration elements

An organization may require multiple site templates designed for each department or team. Each template may contain it’s own navigation, libraries and features associated with them. To build such templates we normally tend to create multiple site definitions.

But that’s not the best approach to fulfill the requirement. Instead of multiple site definitions, we can create multiple configuration items within the same site definition.

Following scenario shows the onet.xml file of a site definition which contains two configurations created for HR department and Finance department.

image

Given below is the configuration section for ContosoHR which contains custom lists and some features

  1. <Configuration ID="0" Name="ContosoHR">
  2.       <Lists>
  3.         <List
  4.         FeatureId="00BFEA71-E717-4E80-AA17-D0C71B360101"
  5.         Type="101"
  6.         Title="HR Documents"
  7.         Url="$Resources:core,shareddocuments_Folder;"
  8.         QuickLaunchUrl="$Resources:core,shareddocuments_Folder;/Forms/AllItems.aspx" />
  9.       </Lists>
  10.       <SiteFeatures>
  11.         <!-- BasicWebParts Feature -->
  12.         <Feature ID="00BFEA71-1C5E-4A24-B310-BA51C3EB7A57" />
  13.         <!-- Three-state Workflow Feature -->
  14.         <Feature ID="FDE5D850-671E-4143-950A-87B473922DC7" />
  15.       </SiteFeatures>
  16.       <WebFeatures>
  17.         <!-- TeamCollab Feature -->
  18.         <Feature ID="00BFEA71-4EA5-48D4-A4AD-7EA5C011ABE5" />
  19.         <!-- MobilityRedirect -->
  20.         <Feature ID="F41CC668-37E5-4743-B4A8-74D1DB3FD8A4" />
  21.         <!-- WikiPageHomePage Feature -->
  22.         <Feature ID="00BFEA71-D8FE-4FEC-8DAD-01C19A6E4053" />
  23.       </WebFeatures>
  24.       <Modules>
  25.         <Module Name="DefaultBlank" />
  26.       </Modules>
  27.     </Configuration>

Then we need to modify the web template file (webTemp_ContosoSites.xml file in this example) to include our custom configurations.

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Templates xmlns:ows="Microsoft SharePoint">
  3.   <Template Name="ContosoSites" ID="10002">
  4.     <Configuration ID="0"
  5.                    Title="Contoso HR Site"
  6.                    Hidden="FALSE"
  7.                    ImageUrl="/_layouts/images/CPVW.gif"
  8.                    Description="Contoso HR Site"
  9.                    DisplayCategory="Contoso Sites">
  10.     </Configuration>
  11.     <Configuration ID="1"
  12.                    Title="Contoso Finance Site"
  13.                    Hidden="FALSE"
  14.                    ImageUrl="/_layouts/images/CPVW.gif"
  15.                    Description="Contoso HR Site"
  16.                    DisplayCategory="Contoso Sites">
  17.     </Configuration>
  18.   </Template>
  19. </Templates>

After deploying the site definition, we can see multiple templates available under “Contoso Sites” section

image

By doing this we can properly group our site templates. Furthermore this is the way how SharePoint groups it’s default site templates (e.g.: Team Site, Blank Site, etc..)

Saturday, March 15, 2014

Best practice for using configuration values in SharePoint

If we need to keep configuration values for SharePoint customizations, the easiest way is to store them in AppSettings section in configuration file (web.config, owstimer.exe.config, etc..). Following is a sample of the usage.
<appSettings>
    <add key="ConfigSite" value="/sites/config" />
    <add key="AppPoolUser" value="dev\spadmin" />
    <add key="ProjectList" value="Projects" />
</appSettings>

But there are some limitations in that approach
  • Settings are not stored in any database. Hence If we stop the “Microsoft SharePoint Foundation Web Application” service in multi server environment web applications will be automatically created in another server but without configuration values.
  • We have to set configuration values in all WFE servers.
  • Modification of configuration files is always risky
To overcome above limitations we can choose from two alternative solutions. One is to use SPWebConfigModification object to programmatically set configuration values.

But I personally prefer the next approach, which is to use web application properties. It is very simple and the best approach to handle external values within the web application. Furthermore we can simply set and modify those values from a PowerShell interface.

Following is the way to set web application properties

  1. $webApp = "http://sp2013/"
  2. $app = Get-SPWebApplication $webApp

  3. $app.Properties.Add("ConfigSite","/sites/config")
  4. $app.Properties.Add("AppPoolUser","dev\spadmin")
  5. $app.Properties.Add("ProjectList","Projects")
  6. $app.Update();

Following is a sample usage of web application properties within an event receiver

  1. public override void FeatureActivated(SPFeatureReceiverProperties properties)
  2. {
  3.   var web = properties.Feature.Parent as SPWeb;
  4.   var webApplication = web.Site.WebApplication;
  5.   var leftNavOrder = webApplication.Properties["ProjectList"].ToString();

Monday, March 10, 2014

Deploy site definitions to both SharePoint 2010 and SharePoint 2013 experience versions

When we deploy a site definition in SharePoint 2013, it will be deployed to 15 hive. Hence it will be listed in the template selection section. But as expected it will not be listed if we select the experience version as 2010

image

But what if we need the template to be available in both 2010 and 2013 experience versions. It’s very simple, we just need to specify the CompatibilityLevel switch when deploying the solution. To learn more on compatibility levels you can refer this article

  1. Add-SPSolution -LiteralPath "C:\Deploy\STSTest.wsp"
  2. Install-SPSolution -Identity STSTest.wsp -CompatibilityLevel14,15” -GACDeployment -force

Then the template will be available for 2010 experience version as well

image

Tuesday, March 4, 2014

SPSiteDataQuery to aggregate results from multiple lists

If we need to execute a query against multiple lists to aggregate results, the best approach is to use SPSiteDataQuery.

Following is a sample code of the usage. In this scenario I want to get all documents created by user “spadmin” those reside anywhere within the site collection. So the query should execute against all document libraries within all sites under the site collection

  1. using (var site = new SPSite("http://sp13:8080/sites/8674"))
  2. {
  3.   using (var web = site.RootWeb)
  4.   {
  5.     var query = new SPSiteDataQuery();
  6.     query.Webs = "<Webs Scope=\"Site Collection\">";
  7.     query.Lists = "<Lists ServerTemplate=\"101\" />";
  8.     query.ViewFields = "<FieldRef Name=\"Title\" />";
  9.     query.Query = "<Where><Eq><FieldRef Name=\"Author\"/><Value Type='User'>spadmin</Value></Eq></Where>";
  10.     DataTable table = web.GetSiteData(query);
  11.   }
  12. }

There are 3 configurations for the Webs Scope setting.

  • Not Set : This is the default setting. If this is the case, it will query inside the current web only
  • Recursive : Query inside current web and all its sub webs
  • Site Collection : Query inside all webs within the site collection

Sunday, March 2, 2014

Programmatically avoid sending queries to other search web parts on the same page

Let’s assume that we have a SharePoint page where we have multiple search web parts.

In this example I have a “Search Result” web part as well as a “Search Box” web part those are added to the page programmatically. According to the requirement, those two web part should work independently.

image

But if we search using the Search Box, it searches through the Search Result web part and not search the entire site. So it’ll not redirect me to the search results page as expected.

image

If we check the web part properties of the Search Box web part, we can identify the problem.

image

Currently it is set to obtain results from Search Results web part. But we need to select the other option, which is “Send queries to custom page URL”.

How we can do this programmatically?

It is very simple. We need to set the TryInplaceQuery property to false.

  1. var searchBoxScriptWebPart = new SearchBoxScriptWebPart();
  2. searchBoxScriptWebPart.TryInplaceQuery = false;

If we search using Search Box web part, now it redirects to a search result page. Now search web parts work independently as expected.

image