George Durzi

in

December 2008 - Posts

Anonymously accessible splash page in a SharePoint publishing site

I'm currently working on an anonymously-accessible internet SharePoint website with a splash page for the home page, i.e. it has a completely different look and feel than the rest of the site.

The way we implemented this was to build a separate master page and page layout for the home splash page, where the page layout would override the default master page in its code behind.

When a page layout has a code behind, its declarations are a little different than your typical page layout that inherits from PublishingPage

   1:  <%@ Assembly Name="PageLayouts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=" %>
   2:   
   3:  <%@ Page Language="C#" Inherits="PageLayouts.HomeSplash" %>

In the code behind of the file, we override the site's master page in the OnPreInit event:

   1:      public class HomeSplash : LayoutsPageBase
   2:      {
   3:          protected override void OnPreInit(EventArgs e)
   4:          {
   5:              this.MasterPageFile = "homesplash.master";
   6:          }
   7:      }

However, when extended the site into the Internet zone and enabled anonymous authentication (via forms), pages created based on the HomeSplash page layout where triggering the site to ask the user to login.

Turns out that LayoutsBasePage isn't the right type to use if you want this page to be anonymously accessible, instead the page layout needs to inherit from UnsecuredLayoutsBasePage.

   1:      public class HomeSplash : UnsecuredLayoutsPageBase
   2:      {
   3:          protected override bool AllowAnonymousAccess { get { return true; } }
   4:   
   5:          protected override void OnPreInit(EventArgs e)
   6:          {
   7:              this.MasterPageFile = "homesplash.master";
   8:          }
   9:      }

We also override a property of UnsecuredLayoutsBasePage called AllowAnonymousAccess to denote that pages based on this page layout should be anonymously accessible.

SharePoint Feature to Provision Robots.txt File

If you've worked on a public facing internet site, you're probably familiar with robots.txt; a text file that you place in the root of your site to instruct spiders (at least the well-behaved ones) to not crawl certain parts of your site.

For more information about robots.txt, check out http://www.robotstxt.org/.

If your public facing SharePoint site was based on the Publishing site definition, anonymous access to certain parts of the site is locked down by default.

This isn't always the case though ... For example, you can browse to http://www.msdndevcon.com/Pages/Forms/AllItems.aspx, which is a SharePoint system page that spiders and users should never see.

As with all custom SharePoint development, there are several ways to accomplish something. I'm going to describe how to provision a robots.txt file to your web site using a SharePoint Feature.

In practice, you can use a stapling feature to staple the ProvisionRobotsTXT feature to an out-of-the-box or custom site definition.

manifest.xml

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <Solution xmlns="http://schemas.microsoft.com/sharepoint/"
   3:    SolutionId="{DDEE7ACD-3A78-49b7-BDA3-4986A5970598}"
   4:    DeploymentServerType="WebFrontEnd"
   5:    ResetWebServer="FALSE">
   6:    <Assemblies>
   7:      <Assembly DeploymentTarget="GlobalAssemblyCache" 
   8:                Location="ProvisionRobotsTXT.dll" />
   9:    </Assemblies>
  10:    <FeatureManifests>
  11:      <FeatureManifest Location="ProvisionRobotsTXT\feature.xml"/>
  12:    </FeatureManifests>
  13:  </Solution>

feature.xml

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <Feature xmlns="http://schemas.microsoft.com/sharepoint/"
   3:    Id="B48576E5-63F4-44e0-9BD7-88E341A0C432"
   4:    Title="Provision Robots.txt"
   5:    Hidden="FALSE"
   6:    Scope="Web"
   7:    Version="1.0.0.0"
   8:    ReceiverClass="FeatureReceiver"
   9:    ReceiverAssembly="ProvisionRobotsTXT, Version=1.0.0.0, Culture=neutral, PublicKeyToken=">
  10:  </Feature>

We're going to use a SharePoint Feature Receiver to provision the robots.txt file when this feature is activated.

FeatureReceiver

   1:  public class FeatureReceiver : SPFeatureReceiver
   2:  {
   3:      public override void FeatureActivated(
   4:          SPFeatureReceiverProperties properties)
   5:      {
   6:          using (SPWeb web = properties.Feature.Parent as SPWeb)
   7:          {
   8:              string siteUrl = web.Site.Url;
   9:   
  10:              SPSecurity.RunWithElevatedPrivileges(
  11:                  delegate()
  12:                  {
  13:                      RobotsGenerator.GenerateRobotsTXT(siteUrl);
  14:                  });
  15:          }
  16:      }

When the ProvisionRobotsTXT feature is activated, the FeatureActivated method of the FeatureReceiver triggers the creation of the robots.txt file in the root of the web application that the feature was activated in.

GenerateRobotsTXT

   1:  public static void GenerateRobotsTXT(string siteUrl)
   2:  {
   3:      try
   4:      {
   5:          StringBuilder textWriter = new StringBuilder(string.Empty);
   6:   
   7:          using (SPSite site = new SPSite(siteUrl))
   8:          {
   9:              using (SPWeb web = site.RootWeb)
  10:              {
  11:                  textWriter.AppendLine("User-Agent: *");
  12:                  textWriter.AppendLine("Disallow: /Search/");
  13:                  textWriter.AppendLine("Disallow: /_layouts/");
  14:                  textWriter.AppendLine("Disallow: /ReusableContent/");
  15:                  textWriter.AppendLine("Disallow: /Reports%20List/");
  16:                  textWriter.AppendLine("Disallow: /WorkflowTasks/");
  17:                  textWriter.AppendLine("Disallow: /SiteCollectionDocuments/");
  18:                  textWriter.AppendLine("Disallow: /SiteCollectionImages/");
  19:                  textWriter.AppendLine("Disallow: /Documents/Forms/");
  20:                  textWriter.AppendLine("Disallow: /Pages/Forms/");
  21:   
  22:                  textWriter.AppendLine("Sitemap: sitemap.xml");
  23:   
  24:                  MemoryStream stream = new MemoryStream(
  25:                      Encoding.UTF8.GetBytes(textWriter.ToString()));
  26:                  web.Files.Add("robots.txt", stream, true);
  27:   
  28:                  stream.Close();
  29:   
  30:                  if (stream != null)
  31:                      stream.Dispose();
  32:              }
  33:          }
  34:      }
  35:   
  36:      catch (Exception ex)
  37:      {
  38:          throw (ex);
  39:      }
  40:  }

The GenerateRobotsTXT method creates the text of the robots.txt file and adds it to the root of the web.

Robots.ddf

   1:  .OPTION Explicit
   2:  .Set DiskDirectoryTemplate=CDROM
   3:  .Set CompressionType=MSZIP
   4:  .Set UniqueFiles=Off
   5:  .Set Cabinet=On
   6:  ;**************************************************
   7:  DeploymentFiles\manifest.xml
   8:  DeploymentFiles\ProvisionRobotsTXT.dll
   9:   
  10:  .Set DestinationDir=ProvisionRobotsTXT
  11:  TEMPLATE\FEATURES\ProvisionRobotsTXT\feature.xml

Use this ddf file to create the WSP for your SharePoint solution. My favorite way of doing this is to add a custom build target to the project that runs MAKECAB when you compile the solution. Of course, there are other great tools out there such as WSPBuilder which make this a little easier easier.

Deployment Script

   1:  @SET STSADM="c:\program files\common files\microsoft shared\web server extensions\12\bin\stsadm.exe"
   2:  @SET WSPNAME="ProvisionRobotsTXT.wsp"
   3:   
   4:  %STSADM% -o addsolution -filename %WSPNAME%
   5:  %STSADM% -o deploysolution -name %WSPNAME% -allowGacDeployment -immediate
   6:  %STSADM% -o execadmsvcjobs
   7:   
   8:  PAUSE

Wrapping Up

One might argue that this solution is overkill, after all, you can open SharePoint Designer and easily create the robots.txt file in there in a few seconds. 

Probably the loudest complaint from the SharePoint development community is that it's hard to integrate development best practices into custom SharePoint development. By wrapping this functionality into a feature, you can integrate it into your development process.

I've used a feature stapling feature to staple this feature to a custom site definition. For example, when I provision a site based on a custom publishing site definition, the ProvisionRobotsTXT feature is automatically activated and the robots.txt file is provisioned to the site.

Best of all, you don't need to add a manual step to your deployment process to create the robots.txt file in SharePoint Designer.