Sue Hernandez's SharePoint Blog

SharePoint and Related Stuff

Tag Archives: SharePoint

Repost – Get an SPUser Object From a List or Library in SharePoint Using PowerShell (Brandon Atkinson)

This blog was written by someone else but appears to not be available right now.  I got this on cache.  I am re-posting it cause I had the hardest time tracking this down.  On reflection, it makes sense that you’d have to do it very similar to C# code, but I was stuck for a while.

THIS POST WAS WRITTEN BY BRANDON ATKINSON not me.  Following is the link, however it appears to not be working right now.  sharepointbrandon.com/category/powershell

I was looking this up for SharePoint 2010, however it most likely works the same in 2013.

Get an SPUser Object From a List or Library in SharePoint Using PowerShell

At some point in your journeys with PowerShell and SharePoint you’ll need to pull a user from a list/library column and get an email address or login name, etc.  If you’ve never done this before it can be a little frustrating as you have to jump through a couple of hoops.  Let’s take a look at simply querying the list.

Let’s assume we have a library with a Name column and a user column called “Owner”.  Now we can easily query the library and pull out the “Name” with something like this:

# $docLib is a reference to a library and $spQuery is an SPQuery object with a CAML query
$spListItems = $docLib.GetItems($spQuery)

# Loop through items and write out info
foreach ($item in $spListItems) {

    Write-Host $item.Name
}

In this example we simply query the library and loop through the results, writing out the ”Name”.  But what if we needed to also write out the email address of the user column.  Here is where the hoops come in, fortunately they are small.

You have to perform some casts in order to get the actual SPUser object.  So taking the previous example, we can re-write it like this:

# $docLib is a reference to a library and $spQuery is an SPQuery object with a CAML query
$spListItems = $docLib.GetItems($spQuery)

# Loop through items and write out info
foreach ($item in $spListItems) {

     Write-Host $item.Name

    # Get the SPUser object from the column
$spFieldUser = [Microsoft.SharePoint.SPFieldUser]$item.Fields.GetField(“Owner”);


$spFieldUserValue = [Microsoft.SharePoint.SPFieldUserValue]$spFieldUser.GetFieldValue($item[“Owner”].ToString());

$user = $spFieldUserValue.User;

   # Pull out the user email
Write-Host = $user.Email
}

In this example we first have to create an SPFieldUser object using $item.Fields.GetField(“Owner”).  Once we have that reference we can use the GetFieldValue method to return an SPFieldUserValue object, which will contain the actual SPUser object.  Now we have access to all the properties on the user like Email, DisplayName, LoginName, etc.

As you can see, its pretty easy to get to the user object in SharePoint via PowerShell.

Enjoy!

Advertisements

CAML vs LINQ to SharePoint 2010 – Performance

Summary

LINQ to SharePoint is really cool.  There’s lots of things you can do with it that you can’t do with CAML alone.  It’s much easier to read, to work with, to maintain, and because of the strongly typed objects, you find more of your bugs at compile time than at run time like you have to with CAML.

However, let me just say LINQ to SharePoint performance STINKS big time when you’re doing relatively simple comparisons that you could just as easily do in straight CAML.

My Test Case

My test case is a program that reads in information from 2 different stored procedures in SQL, separates out the data, loads them in to 4 SharePoint Lists, and looks for invalid corresponding data in another SharePoint list.  Basically 1 list is a master Valid Codes list, 1 list is a master Invalid Codes list, and the other 2 lists are distinct lists of categories and departments that relate to those codes, coming from the denormalized SQL queries.  Basically you first put all the valid codes in to the valid list, then you go through the invalid codes and see if there are matching codes in the valid codes list, and if so delete them, then check for corresponding data in another SharePoint list that has those code values in it, then put the invalid code in the invalid list.

The results

Running the program using straight CAML queries and no LINQ took 3 hours 11 minutes.

Running the program using all LINQ to SharePoint and no CAML took 9 hours 2 minutes.

 

I’m really bummed, I liked LINQ.  Now I’m not so sure.

InfoPath 2010 – Registering of form templates deployed through object model is not supported

In an earlier post, https://suehernandez.wordpress.com/2013/11/22/deploy-infopath-2010-with-code-as-solution-feature/, I explained how to implement an InfoPath 2010 form as a Feature in SharePoint 2010.

As I was trying to deploy this feature, I kept getting the error:

Registering of form templates deployed through object model is not supported

 

It turns out that what happened is that up until that point I had been deploying my InfoPath forms through Central Administration.  It’s easiest and usually quickest to do it this way (although once you deploy the same form like 50 times it starts getting really slow to deploy).

But I had not changed the “Identifier” per se of the form.  Let me show you a couple of pictures that shows what I’m talking about:

Form Template Properties buttonForm Template Properties dialog

Where it says “urn:schemas-microsofr-com….” that is the identifier of the form.  If you publish the form, and then copy it and change the name of it, it does NOT change the identifier.  So if you’ve already got that ID in Central Admin under Manage Form Templates, and then you try to deploy the same form as a feature, you get this error message.

One of the ways you can fix this is to either manually change this URN ID, or just open up the form in InfoPath Designer in design mode and do a Save-As.  It will automatically change your ID to match the new name of the form.

In the mean time, delete the form from Central Admin and try your Solution Deployment again.

Deploy InfoPath 2010 with Code as Solution-Feature

This one will be somewhat long of a post.   Here we will examine deploying an InfoPath 2010 form with CodeBehind as a custom Feature to SharePoint 2010.

  1. Change the properties of your form to Full Trust

    Full Trust

    Full Trust

  2. Publish your form to a SharePoint Server

    Publish Step 1

    Publish Step 1

  3. Choose your URL to the site where you’re going to be using the file.  Note that it will put this form in the Form Templates (url is FormServerTemplates) library at the top of your Site Collection

    Publish Step 2

    Publish Step 2

  4. Make sure that it is checked off to enable the form to be filled out by a browser, and choose Administrator Approved Template

    Publish Step 3

    Publish Step 3

  5. Choose the location on your desktop or local computer or shared drive where you want to save the Published form template to

    Publish Step 4

    Publish Step 4

  6. Create your Visual Studio solution.  Create it as a SharePoint 2010 Empty SharePoint Project

    Create new Empty SP Project

    Create new Empty SP Project

  7. Choose to deploy as Farm Solution

    Deploy as Farm Solution

    Deploy as Farm Solution

  8. Add a Feature to the project

    Add Feature

    Add Feature

  9. Rename the feature to something that makes more sense, so you can more easily find it later through PowerShell or in the 14 hive

    Rename Feature

    Rename Feature

  10. Open the feature in Visual Studio into Design view and set the properties of the feature at the top.

    Set Properties of Feature

    Set Properties of Feature

  11. Add a Module to the project

    Add Module

    Add Module

  12. Rename the module and remove the Sample.txt file

    Rename Module and remove Sample txt

    Rename Module and remove Sample txt

  13. Add your Module Files.  Here you will be adding the published form that you completed in Step 5 here, as well as the dll.  (You can get the .dll by going into design view in InfoPath, pressing Publish, and pressing Export Source Files – give it a folder to put the source files in, and find your .dll in there)

    Note that I have a little naming change here versus what I have in step 5.  I am showing you a “real” project file and had to blot out part of the name.  But this will be your published file – not the file you use to make the changes to the form.

    Add Module Files

    Add Module Files

  14. Open the Elements file in the NewFormModule.  You will have to make changes.  Here’s the file as it looks before:
    Elements Before

    Elements Before

    and here’s the file as it looks after:

    Elements After

    Elements After

  15. Open back up the Feature in Design mode, and press the button to switch it to Manifest mode.  Expand Edit Options and add the 2 properties shown below, with your Feature Name and your Published InfoPath form name as the values:

    Update the Manifest

    Update the Manifest

  16. Get the Properties window up for the Feature.  You will be adding a Receiver Assembly and a Receiver Class
    ReceiverAssembly=”Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”
    ReceiverClass=”Microsoft.Office.InfoPath.Server.Administration.XsnFeatureReceiver”

    Feature Props add Receiver info

    Feature Props add Receiver info

  17. OPTIONAL:  Add Activation dependency.  NOTE:  if you are going to mark your feature as Hidden=TRUE, you cannot add this dependency.  We will discuss later why you may want to make your feature Hidden.
    <ActivationDependency FeatureId=”C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5″/>

    Feature Activation Dependencies

    Feature Activation Dependencies

  18. Now in your solution explorer, you will want to highlight the Elements file and get the properties.  Clear out the Deployment Path and make sure it’s of type ElementManifest

    Elements Properties

    Elements Properties

  19. Next highlight the XSN published form and get its properties.  Clear the Deployment Path and make sure it is of type ElementFile

    Form Properties

    Form Properties

  20. Repeat the previous step for the DLL file
  21. Open up your Feature in Design mode just to look at it.  Your Manifest file should look similar to the following (if you added the Activation Dependency):

    Manifest After

    Manifest After

Making the feature Hidden

The problem with adding the published file to Central Administration – Managed Form Templates is twofold:  (1) It puts in every version you create into the manifest; and (2) It makes the feature available to EVERY site collection in your Farm.  Similarly, if you don’t make the feature Hidden, all site collections will be able to see this feature.

Depending on your project, you may opt to hide the feature so that no one can turn it on, and only activate it to the Site Collection using PowerShell.

If you choose to make your feature hidden, you cannot have the Activation Dependency of the Enterprise Features.  Just realize this means is that if you don’t have Enterprise Features turned on, this Content Type will not work.

Here’s a picture of where you’d make the feature hidden – it’s just another one of the properties on the feature:

Is Hidden is True

Is Hidden is True

Deploy the feature, Activate it, and Use it

Once you deploy the solution package (.WSP file) and activate it to the site collection you’re going to use it in, it will now be a Content Type that you add to your Form Library.  Any fields which you promoted in the Publishing process will automatically be added to your Form Library and will sync to the data in the form.

References

I got bits and pieces from a lot of places, but here’s some main ones that I got a lot of detail from:

SPSecurity.RunWithElevatedPrivileges and Console Applications

Wow – I should have recognized this a LONG time ago.  But hey, I’m human.

When you use code such as the following:

SPSecurity.RunWithElevatedPrivileges(delegate()
{

     using (SPSite site = newSPSite(urlToSite))
{

         using (SPWeb web = site.OpenWeb())
{

             // Do something
         }
}
});

if you use that code in a Web Part, Application Page, or I think Event Handler – i.e. any code that has Context inside SharePoint – the code runs as expected and will elevate your permissions to the Application Pool account.

However, if you try to use that same code in your Console application, and let’s say you update a list item, and you run it either in debug mode or just “regularly” by double-clicking the exe file, you will notice that last modified by is YOU – NOT your Application Pool account.

This is because there’s no SharePoint context.

Instead, you can create a batch file (like if you have input parameters) and use the “RunAs” method that Windows gives you (example, in Windows Server 2008 R2 you have to hold down Shift and Right-click the batch file, in order to get the Run As option).  If you’re setting up the job as a scheduled task through Windows, you simply have to set the account to the Application Pool account and specify that it should run whether or not the account is logged in.

MOSS – SPGridView with CheckBox Column

In this article, we will explore using the SPGridView control, and adding an editable checkbox column, with JavaScript that allows us to “Select All”.   The full code is at the bottom of the article. I used the WSPBuilder tool to create the project and add the Solution information (i.e. the Feature File, elements file, etc) automatically.

In this example, we have a user cleanup task; we have a list of domain account names that have matching domain account names in another domain.  The stored procedure does the heavy lifting, in examining the account names and determining if there are “matches” based on a set of criteria.  A Requesting user will input their account name, and press Search, and then they will get a list of all users who are members of every site on which the Requesting user has administrative access.  So in short, all their users for their sites they admin.  We will then give them a button which, when clicked, will perform some action on these users, as long as they are checked.

NOTE:  In this article, we will not explore putting the grid in Edit mode, nor paging, nor sorting.  Please read the reference articles for some good tips.

References

First of all, there are many good articles out there explaining the need to instantiate the SPGridView control in the CreateChildControls method, and to set your databinding in the OnLoad event, as long as the page is not posting back.

protected override void CreateChildControls()
  {
   if (!_error)
   {
    try
    {
     base.CreateChildControls();

     // Add spacer
     this.Controls.Add(new LiteralControl("<br />"));

     // Add TextBox and Search Button
     txtUserName = new TextBox();
     this.Controls.Add(txtUserName);
     cmdSearch = new Button();
     cmdSearch.Text = "Search";
     cmdSearch.Click += new EventHandler(cmdSearch_Click);
     this.Controls.Add(new LiteralControl("&nbsp;&nbsp;&nbsp;"));
     this.Controls.Add(cmdSearch);

     // Add spacer and Select All
     this.Controls.Add(new LiteralControl("<br /><br />"));
     this.Controls.Add(new LiteralControl("<a href='javascript:SelectAllCheckBoxes();'>Select/Deselect All</a><br />"));

     // Add GridView
     gridView = new SPGridView();
     CreateBoundFields(ref gridView);
     this.Controls.Add(gridView);

     // Add spacer
     this.Controls.Add(new LiteralControl("<br />"));

     // Add Submit Button
     cmdSubmit = new Button();
     cmdSubmit.Text = "Submit";
     cmdSubmit.Click += new EventHandler(cmdSubmit_Click);
     cmdSubmit.OnClientClick = "return confirm('Are you sure?');";
     this.Controls.Add(cmdSubmit);

     // Add Javascript
     this.Controls.Add(GetJavaScript());
    }
    catch (Exception ex)
    {
     HandleException(ex);
    }
   }
  }
 

Here, we first add a text box and a Search button, so that our user may input an Active Directory account name, which will be used in the data binding when we call the stored procedure.  We add an anchor tag with a javascript function, which is what we’ll use to toggle between Selected and Unselected.   Next we instantiate the SPGridView.  Notice that we did not set the variable here – the variable is in the private variable section, but we don’t instantiate until we get into CreateChildControls.  After that, we add our “Submit” button, which will perform whatever action you want to perform on all of the checked items.  Finally, we add a Literal Control that holds our JavaScript Toggle function.  We use the gridView.ClientID in the JavaScript to identify which input tags are actually the checkboxes that we want to toggle.

The CreateBoundFields method is where we set up our binding, for as it turns out, you always have to set SPGridView.AutoGenerateColumns to false and manually bind the columns to your data source.

  private void CreateBoundFields(ref SPGridView gridView)
  {
   gridView.Columns.Clear();
   gridView.AutoGenerateColumns = false;

   //CheckBoxField cbField = new CheckBoxField();
   //cbField.DataField = "Checked";
   //cbField.HeaderText = "Include";
   //gridView.Columns.Add(cbField);

   TemplateField checkboxCol = new TemplateField();
   checkboxCol.HeaderText = "Include";
   // Call our custom template here
   checkboxCol.ItemTemplate = new CustomSPGridViewTemplate(ListItemType.Item, "Select Item");
   gridView.Columns.Add(checkboxCol);

   BoundField colName = new BoundField();
   colName.DataField = "AccountName";
   colName.HeaderText = "Account Name";
   gridView.Columns.Add(colName);

   colName = new BoundField();
   colName.DataField = "MatchingAccountName";
   colName.HeaderText = "Matching Account Name";
   gridView.Columns.Add(colName);

  }

Notice that I have a commented out section here.  I had originally used a CheckBoxField type and that worked just fine in a sense.  However, number one, it was bound to my data source, so I actually had to create a data table with a “Checked” column in it, and two, you have to enable editing of the SPGridView.  In other words, to change the checkbox column from checked to unchecked or vice versa, you actually had to put the whole row in edit mode (post back), click the checkbox, then update the row (post back).  I just wanted a simple checkbox column that I could easily just check…check…check.

So here what we do, is we actually create a custom Class, inheriting from the ITemplate interface.  I called it CustomSpGridViewTemplate in this case, just like in the article I referenced at the top.  Now contrary to what I think is stated in that article, I am simply instantiating this TemplateField in code, and it’s not giving me any problems, probably because I have not enabled Paging or Sorting.

In the CustomSPGridViewTemplate class, we simply tell the Template what to put when it’s in Header mode, and what to put when it’s in ListItemType mode.  

public class CustomSPGridViewTemplate : ITemplate
 {
  private ListItemType templateType;
  private string columnName;

  public CustomSPGridViewTemplate(ListItemType type, string column)
  {
   this.templateType = type;
   this.columnName = column;
  }

  #region ITemplate Members

  public void InstantiateIn(Control container)
  {
   Literal lc = new Literal();
   switch (templateType)
   {
    case ListItemType.Header:
     lc.Text = "<b>" + columnName + "</b>";
     container.Controls.Add(lc);
     break;
    case ListItemType.Item:
     CheckBox checkBox = new CheckBox();
     checkBox.Checked = true;
     checkBox.ID = "CustomCheckboxID";
     checkBox.Visible = true;
     container.Controls.Add(checkBox);
     break;
   }
  }

  #endregion
 }

When the user clicks the submit button, we simply iterate through the GridViewRowCollection of the SPGridView.  The first cell will be the checkbox column in this example, so we simply get the first control in that cell and cast it as a CheckBox object.  Then we can tell if it’s checked or unchecked.  Next, I also output the text that was in the next cell, which contains my list of domain Account Names.

  private void cmdSubmit_Click(object sender, EventArgs e)
  {
   this.Controls.Add(new LiteralControl("<br><br>Submit Pressed"));

   foreach (GridViewRow row in gridView.Rows)
   {
    CheckBox cb = row.Cells[0].Controls[0] as CheckBox;
    this.Controls.Add(new LiteralControl("<br>" + cb.Checked.ToString() + " " + row.Cells[1].Text));
   }
  }

Finally, we output our JavaScript.

private LiteralControl GetJavaScript()
  {
   StringBuilder sb = new StringBuilder();

   sb.Append("\n\n");
   sb.Append("<script language='javascript'>\n");
   sb.Append("   function SelectAllCheckBoxes() \n");
   sb.Append("   { \n");
   sb.Append("      var checkboxes = document.getElementsByTagName('input'); \n");
   sb.Append("      for(var i=0; i<checkboxes.length; i++) \n");
   sb.Append("      { \n");
   sb.Append("         // Look for a CheckBox \n");
   sb.Append("         var checkbox = checkboxes[i]; \n");
   sb.Append("         \n");
   sb.Append("         // Verify it's the right name \n");
   sb.Append("         var start = '" + gridView.ClientID + "'; \n");
   sb.Append("         var end = 'CustomCheckboxID'; \n");
   sb.Append("         var startsWith = checkbox.id.match('^'+start)==start; \n");
   sb.Append("         var endsWith = checkbox.id.match(end+'$')==end; \n");
   sb.Append("         \n");
   sb.Append("         // Make the switch \n");
   sb.Append("         if(startsWith && endsWith) \n");
   sb.Append("         { \n");
   sb.Append("            checkbox.checked = !checkbox.checked; \n");
   sb.Append("         } \n");
   sb.Append("      } \n");
   sb.Append("   } \n");
   sb.Append("</script>\n");

   return new LiteralControl(sb.ToString());
  }

We first iterate through all elements with a tag name of “input”.  I want to ensure that we have the right checkboxes, so I do a string comparison – I ensure that it starts with the ClientID of the SPGridView Control, and that it ends with the control name that I specified in the custom Class, in this case “CustomCheckboxID”.  If it matches that criteria, I simply toggle the checkbox from checked to unchecked or vice versa.  I could also have done something like looked at the link we clicked and if it said “Select All” , to make sure all the checkboxes were checked, and then change the link’s text to “Deselect All”. 

Following is the full code that the article references.

 [Guid("5cd4210a-6816-4ec2-959e-78b01347df8c")]
 public class MultiDomainUserMatchup : Microsoft.SharePoint.WebPartPages.WebPart
 {
  private bool _error = false;
  private SqlDataReader rdr = null;
  private SPGridView gridView;
  private Button cmdSubmit;
  private Button cmdSearch;
  private TextBox txtUserName;

  public MultiDomainUserMatchup()
  {
   this.ExportMode = WebPartExportMode.All;
  }

  #region Overrides and Event Handlers

  /// <summary>
  /// Create all your controls here for rendering.
  /// Try to avoid using the RenderWebPart() method.
  /// </summary>
  protected override void CreateChildControls()
  {
   if (!_error)
   {
    try
    {
     base.CreateChildControls();

     // Add spacer
     this.Controls.Add(new LiteralControl("<br />"));

     // Add TextBox and Search Button
     txtUserName = new TextBox();
     this.Controls.Add(txtUserName);
     cmdSearch = new Button();
     cmdSearch.Text = "Search";
     cmdSearch.Click += new EventHandler(cmdSearch_Click);
     this.Controls.Add(new LiteralControl("&nbsp;&nbsp;&nbsp;"));
     this.Controls.Add(cmdSearch);

     // Add spacer and Select All
     this.Controls.Add(new LiteralControl("<br /><br />"));
     this.Controls.Add(new LiteralControl("<a href='javascript:SelectAllCheckBoxes();'>Select/Deselect All</a><br />"));

     // Add GridView
     gridView = new SPGridView();
     CreateBoundFields(ref gridView);
     this.Controls.Add(gridView);

     // Add spacer
     this.Controls.Add(new LiteralControl("<br />"));

     // Add Submit Button
     cmdSubmit = new Button();
     cmdSubmit.Text = "Submit";
     cmdSubmit.Click += new EventHandler(cmdSubmit_Click);
     cmdSubmit.OnClientClick = "return confirm('Are you sure?');";
     this.Controls.Add(cmdSubmit);

     // Add Javascript
     this.Controls.Add(GetJavaScript());
    }
    catch (Exception ex)
    {
     HandleException(ex);
    }
   }
  }

  /// <summary>
  /// Ensures that the CreateChildControls() is called before events.
  /// Use CreateChildControls() to create your controls.
  /// </summary>
  /// <param name="e"></param>
  protected override void OnLoad(EventArgs e)
  {
   if (!_error)
   {
    try
    {
     base.OnLoad(e);
     this.EnsureChildControls();

     // Your code here...
     if (!Page.IsPostBack)
     {
      BindGrid("AKG\noUser");
     }
    }
    catch (Exception ex)
    {
     HandleException(ex);
    }
   }
  }

  /// <summary>
  /// Submit Button event handler
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void cmdSubmit_Click(object sender, EventArgs e)
  {
   this.Controls.Add(new LiteralControl("<br><br>Submit Pressed"));

   foreach (GridViewRow row in gridView.Rows)
   {
    CheckBox cb = row.Cells[0].Controls[0] as CheckBox;
    this.Controls.Add(new LiteralControl("<br>" + cb.Checked.ToString() + " " + row.Cells[1].Text));
   }
  }

  /// <summary>
  /// Search Button Event Handler
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void cmdSearch_Click(object sender, EventArgs e)
  {
   BindGrid(txtUserName.Text);
  }
  #endregion

  #region GridView Helper

  private void CreateBoundFields(ref SPGridView gridView)
  {
   gridView.Columns.Clear();
   gridView.AutoGenerateColumns = false;

   //CheckBoxField cbField = new CheckBoxField();
   //cbField.DataField = "Checked";
   //cbField.HeaderText = "Include";
   //gridView.Columns.Add(cbField);

   TemplateField checkboxCol = new TemplateField();
   checkboxCol.HeaderText = "Include";
   // Call our custom template here
   checkboxCol.ItemTemplate = new CustomSPGridViewTemplate(ListItemType.Item, "Select Item");
   gridView.Columns.Add(checkboxCol);

   BoundField colName = new BoundField();
   colName.DataField = "AccountName";
   colName.HeaderText = "Account Name";
   gridView.Columns.Add(colName);

   colName = new BoundField();
   colName.DataField = "MatchingAccountName";
   colName.HeaderText = "Matching Account Name";
   gridView.Columns.Add(colName);

  }

  #endregion

  #region Bind Data

  private void BindGrid(string input)
  {
   // Get Data Fresh
   SqlConnection connection = new SqlConnection("Data Source=YourServer;Initial Catalog=YourDatabaseName;User ID=YourUser;Password=YourPassword");
   SqlCommand cmd = new SqlCommand("lkp_YourStoredProcedure", connection);
   cmd.CommandType = System.Data.CommandType.StoredProcedure;

   cmd.Parameters.Add(new SqlParameter("@userName", CleanInput(input)));

   try
   {
    connection.Open();
    rdr = cmd.ExecuteReader();

    if (rdr != null)
    {
     if (rdr.HasRows)
     {
      gridView.DataSource = rdr;
      gridView.DataBind();
     }
    }
   }
   finally
   {
    try { connection.Close(); }
    catch { }
   }
  }

  #endregion

  #region Helpers

  /// <summary>
  /// Clean the input of a text box for use in SQL Parameter
  /// </summary>
  /// <param name="input"></param>
  /// <returns></returns>
  private string CleanInput(string input)
  {
   // Clean the input for SQL Injection
   return input.Replace("'", "''");
  }

  /// <summary>
  /// Get page Javascript
  /// </summary>
  /// <returns></returns>
  private LiteralControl GetJavaScript()
  {
   StringBuilder sb = new StringBuilder();

   sb.Append("\n\n");
   sb.Append("<script language='javascript'>\n");
   sb.Append("   function SelectAllCheckBoxes() \n");
   sb.Append("   { \n");
   sb.Append("      var checkboxes = document.getElementsByTagName('input'); \n");
   sb.Append("      for(var i=0; i<checkboxes.length; i++) \n");
   sb.Append("      { \n");
   sb.Append("         // Look for a CheckBox \n");
   sb.Append("         var checkbox = checkboxes[i]; \n");
   sb.Append("         \n");
   sb.Append("         // Verify it's the right name \n");
   sb.Append("         var start = '" + gridView.ClientID + "'; \n");
   sb.Append("         var end = 'CustomCheckboxID'; \n");
   sb.Append("         var startsWith = checkbox.id.match('^'+start)==start; \n");
   sb.Append("         var endsWith = checkbox.id.match(end+'$')==end; \n");
   sb.Append("         \n");
   sb.Append("         // Make the switch \n");
   sb.Append("         if(startsWith && endsWith) \n");
   sb.Append("         { \n");
   sb.Append("            checkbox.checked = !checkbox.checked; \n");
   sb.Append("         } \n");
   sb.Append("      } \n");
   sb.Append("   } \n");
   sb.Append("</script>\n");

   return new LiteralControl(sb.ToString());
  }

  /// <summary>
  /// Clear all child controls and add an error message for display.
  /// </summary>
  /// <param name="ex"></param>
  private void HandleException(Exception ex)
  {
   this._error = true;
   this.Controls.Clear();
   this.Controls.Add(new LiteralControl(ex.Message));
  }

  #endregion
 }

 public class CustomSPGridViewTemplate : ITemplate
 {
  private ListItemType templateType;
  private string columnName;

  public CustomSPGridViewTemplate(ListItemType type, string column)
  {
   this.templateType = type;
   this.columnName = column;
  }

  #region ITemplate Members

  public void InstantiateIn(Control container)
  {
   Literal lc = new Literal();
   switch (templateType)
   {
    case ListItemType.Header:
     lc.Text = "<b>" + columnName + "</b>";
     container.Controls.Add(lc);
     break;
    case ListItemType.Item:
     CheckBox checkBox = new CheckBox();
     checkBox.Checked = true;
     checkBox.ID = "CustomCheckboxID";
     checkBox.Visible = true;
     container.Controls.Add(checkBox);
     break;
   }
  }

  #endregion
 }

MOSS: Service Pack 2 activates 180-day expiration

According to Twitter and the Microsoft SharePoint Team Blog (http://blogs.msdn.com/sharepoint/archive/2009/05/21/attention-important-information-on-service-pack-2.aspx ):

“During the installation of SP2, a product expiration date is improperly activated. This means SharePoint will expire as though it was a trial installation 180 days after SP2 is deployed. The activation of the expiration date will not affect the normal function of SharePoint up until the expiration date passes. Furthermore, product expiration 180 days after SP2 installation will not affect customer’s data, configuration or application code but will render SharePoint inaccessible for end-users.”

There is a hotfix as well as a workaround for this, all on the MS SP Blog link above.

MOSS – Nested Master Pages

In my first post, Moss Global Custom Master Template, I explained how to create a single custom master page as a feature, and then “Staple” that feature to site definitions (such that it will automatically activate for new sites).

In this post, we’ll talk about the challenges and pitfalls of making a Parent/Child Master Template (a Nested Master Template). The reason we may wish to have a Parent/Child template arrangement is so that we can control a “fixed” area of the page (the Parent), while allowing the Administrators to customize or brand the rest of the page (the Child).

There are 2 ways of approaching this challenge – (1) have an “orphaned” Parent, where the Parent Master page only has minimal code and a placeholder for the bulk of the code; and (2) have a full set of code for both Parent and Child such that they each can stand alone. We will take the second approach.

Design
We will create a template where we can “claim” the very top and the very bottom of the page for our Parent. We will put a custom Web Part in the very top bar, and we will put some disclaimer text and some links on the bottom.

Challenges
We will notice when we create a Parent/Child that there are 2 very big problems: (1) You cannot stop anyone with the appropriate SharePoint Designer rights from unghosting (customizing) both of the master pages; and (2) If you unghost (customize) the Child Master Page (which is the whole point of the excercise), you actually have to also unghost the Parent Master Page, or you break the web site.

The Code
View the HTML code for the templates at the very bottom of the post.

  • Open your site with SharePoint Designer.
  • Open the _catalogs/masterpage folder
  • Right-click on the masterpage folder and select New –> ASPX page.
  • Rename the page YourName.master, where “YourName” is the name you wish to make your custom Parent Master page.
  • Open the YourName.master page, and copy the code in from the Parent Master code sample. Don’t forget, if you copy and paste directly from Word, you get all of Word’s formatting, so I generally copy the code to Notepad first, then copy it over to Designer.
  • Save the page. Right-click the YourName.master page and select “Set as Default Master Page”
  • Set as Default Master Page

    Set as Default Master Page

  • You will be warned that you might break the page. Select OK. (or Yes)
  • Before you do anything with the Child Master page, view the default.aspx page with this master in the browser, and ensure that it loads correctly
    Default.aspx page

    Default.aspx page

  • Here are the images for the readers/viewers in case you like that feature of the Master Page (put them in the Layouts Images folder):
    DOC XLS PDF ZIP PPT
  • Right-click on the masterpage folder and select New –> ASPX page.
  • Rename the page YourName-Child.master, where “YourName” is the same name as your Parent Master page.
  • Open the YourName-Child.master page, and copy the code in from the Child Master code sample.
  • Save the page. Right-click the YourName-Child.master page and select “Set as Custom Master Page” .

All of the code inside the “PlaceHolderMainBase” inside of the Parent gets overridden by the Child. However, we need that code there so that the page is “complete” and standalone – i.e. can function as a master by itself. As many references out there in blog-land will tell you, there are certain placeholders you need by default for the page to work, whether you’re using them or not.

Taking it a step further
OK we did all of this in SharePoint Designer, right? What if we want this to be the default for all new sites, that is where my first article comes into play. We need, however, to make some modifications to the code in the Feature Receiver.

Namely, we have to add variables for BOTH the parent and child masters, as well as to set the web.MasterUrl property to the Parent, and the web.CustomMasterUrl to the Child.

 

 

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
  try
  {
    using (SPWeb web = (SPWeb)properties.Feature.Parent)
    {
      string customURLtoUsePARENT = customizedMasterUrlPARENT;
      string customURLtoUseCHILD = customizedMasterUrlCHILD;

      //Store the old Master URL's and Custom Master URL's
      web.AllProperties["OldMasterUrl"] = web.MasterUrl;
      web.AllProperties["OldCustomMasterUrl"] = web.CustomMasterUrl;
 
      //Assign the Master URL to both properties
      web.MasterUrl = customURLtoUsePARENT;
      web.CustomMasterUrl = customURLtoUseCHILD;

      //Update the Web
      web.Update();
    }
  }
  catch { }
}

You may also wish to remove BOTH files in the FeatureDeactivating function.

Finally, instead of stapling the feature to GLOBAL, we need to specify the templates. Namely, all of them except for the Meeting Workspaces templates. This is due to a SharePoint bug where if you create a meeting workspace from a recurring Calendar event, if you try to staple a feature to the new meeting workspace site, it breaks the scripting on the page such that you cannot switch dates.

<elements xmlns="http://schemas.microsoft.com/sharepoint/">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#2">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="WIKI#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLOG#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BDR#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="EAWF#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="OFFILE#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="OFFILE#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PWA#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PWS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSMSITE#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSTOC#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSTOPIC#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSNEWS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSNHOME#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSSITES#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSBWEB#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSCOMMU#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSREPORTCENTER#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSPORTAL#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SRCHCEN#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PROFILES#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="CMSPUBLISHING#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#2">
</elements>

PARENT MASTER PAGE

<%@Master%>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="wssuc" TagName="Welcome" src="~/_controltemplates/Welcome.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="DesignModeConsole" src="~/_controltemplates/DesignModeConsole.ascx" %>
<HTML dir="<%$Resources:wss,multipages_direction_dir_value%>" runat="server" xmlns:o="urn:schemas-microsoft-com:office:office" __expr-val-dir="ltr">
<HEAD runat="server">
<META Content="Microsoft SharePoint">
<META Content="SharePoint.WebPartPage.Document">
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
<META HTTP-EQUIV="Expires" content="0">
<SharePoint:RobotsMetaTag runat="server"/>
<Title> <asp:ContentPlaceHolder runat="server" /></Title>
<SharePoint:CssLink runat="server"/>
<SharePoint:Theme runat="server"/>
<SharePoint:ScriptLink Defer="true" runat="server"/>
<SharePoint:CustomJSUrl runat="server"/>
<SharePoint:SoapDiscoveryLink runat="server"/>
<SharePoint:DelegateControl runat="server" ControlId="AdditionalPageHead" AllowMultipleControls="true"/>
<asp:ContentPlaceHolder runat="server" />
</HEAD>
<BODY scroll="yes">
<form runat="server">
<WebPartPages:SPWebPartManager runat="Server"/>
<TABLE CELLPADDING="0" CELLSPACING="0" BORDER="0" WIDTH="100%" HEIGHT="100%">
<tr><td><asp:ContentPlaceHolder runat="server">
<!-- ********************************************************
WARNING WARNING WARNING
Do not modify this master page.
********************************************************-->
<table CELLPADDING=0 CELLSPACING=0 BORDER=0 WIDTH="100%">
<tr>
<td colspan=4>
<span style="display:none">
<a href="#">
<SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,master_turnonaccessibility%>" EncodeMethod="HtmlEncode"/></a>
</span>
<A href="javascript:;" AccessKey="<%$Resources:wss,maincontent_accesskey%>" runat="server">
<SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,mainContentLink%>" EncodeMethod="HtmlEncode"/></A>
<table cellpadding=0 cellspacing=0 height=100%>
<tr>
<td style="padding-top: 2px;" height=100% valign=middle>
<div>
<span style="display:none">
<a href="#">
<SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,master_turnoffaccessibility%>" EncodeMethod="HtmlEncode"/></a>
</span>
<asp:ContentPlaceHolder runat="server">
<asp:SiteMapPath SiteMapProvider="SPSiteMapProvider" RenderCurrentNodeAsLink="true" SkipLinkText="" NodeStyle-CssClass="ms-sitemapdirectional" runat="server"/>
</asp:ContentPlaceHolder>
</div>
</td>
</tr>
</table>
<table cellpadding="0" cellspacing="0" height=100%>
<tr>
<td valign="middle" style="padding-left:3px; padding-right:6px;">
<SharePoint:DelegateControl runat="server" ControlId="GlobalSiteLink0"/>
</td>
<td valign="middle" style="padding-right:10px;">
YOUR TEXT OR WEB PART HERE
</td>
<td valign="middle">
<wssuc:Welcome runat="server" EnableViewState="false">
</wssuc:Welcome>
</td>
<td style="padding-left:1px;padding-right:3px;">|</td>
<td valign="middle">
<table cellspacing="0" cellpadding="0">
<tr>
<td>
<SharePoint:DelegateControl ControlId="GlobalSiteLink1" Scope="Farm" runat="server"/></td>
<td>
<SharePoint:DelegateControl ControlId="GlobalSiteLink2" Scope="Farm" runat="server"/></td>
</tr>
</table>
</td>
<td valign="middle">&nbsp;
<a href="javascript:TopHelpButtonClick('NavBarHelpHome')" AccessKey="<%$Resources:wss,multipages_helplink_accesskey%>" title="<%$Resources:wss,multipages_helplinkalt_text%>" runat="server"><img align='absmiddle' border=0 src="/_layouts/images/helpicon.gif" alt="<%$Resources:wss,multipages_helplinkalt_text%>" runat="server"></a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</asp:ContentPlaceHolder></td></tr>
<asp:ContentPlaceHoldermso-bidi-font-weight: normal">PlaceHolderMainBase" runat="server" >
<!-- ********************************************************
The code below is overridden in the Child Master Page
********************************************************-->
<tr><td>
<table width=100% cellpadding=0 cellspacing=0 border=0><tr>
<td>
<SharePoint:SiteLogoImage LogoImageUrl="/_layouts/images/logo.jpg" runat="server"/></td>
<td width=100%>
<asp:ContentPlaceHolder runat="server">
<h1>
<SharePoint:SPLinkButton runat="server" NavigateUrl="~site/">
<SharePoint:ProjectProperty Property="Title" runat="server" />
</SharePoint:SPLinkButton>
</h1>
</asp:ContentPlaceHolder>
</td>
<td style="padding-top:8px;" valign=top>
<asp:ContentPlaceHolder runat="server">
<SharePoint:DelegateControl runat="server" ControlId="SmallSearchInputBox"/>
</asp:ContentPlaceHolder>
</td>
</tr>
</table>
</td>
</tr>
<TR>
<TD WIDTH=100%>
<asp:ContentPlaceHolder runat="server">
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td nowrap valign="middle"></td>
<td-banner width=99% nowrap>
<asp:ContentPlaceHolder runat="server">
<SharePoint:AspMenu
Runat="server"
DataSourceID="topSiteMap"
EnableViewState="false"
AccessKey="<%$Resources:wss,navigation_accesskey%>"
Orientation="Horizontal"
StaticDisplayLevels="2"
MaximumDynamicDisplayLevels="1"
DynamicHorizontalOffset="0"
StaticPopoutImageUrl="/_layouts/images/menudark.gif"
StaticPopoutImageTextFormatString=""
DynamicHoverStyle-BackColor="#CBE3F0"
SkipLinkText=""
StaticSubMenuIndent="0"
CssClass="ms-topNavContainer">
<StaticMenuStyle/>
<StaticMenuItemStyle CssClass="ms-topnav" ItemSpacing="0px"/>
<StaticSelectedStyle CssClass="ms-topnavselected" />
<StaticHoverStyle CssClass="ms-topNavHover" />
<DynamicMenuStyle BackColor="#F2F3F4" BorderColor="#A7B4CE" BorderWidth="1px"/>
<DynamicMenuItemStyle CssClass="ms-topNavFlyOuts"/>
<DynamicHoverStyle CssClass="ms-topNavFlyOutsHover"/>
<DynamicSelectedStyle CssClass="ms-topNavFlyOutsSelected"/>
</SharePoint:AspMenu>
<SharePoint:DelegateControl runat="server" ControlId="TopNavigationDataSource">
<Template_Controls>
<asp:SiteMapDataSource
ShowStartingNode="False"
SiteMapProvider="SPNavigationProvider"
runat="server"
StartingNodeUrl="sid:1002"/>
</Template_Controls>
</SharePoint:DelegateControl>
</asp:ContentPlaceHolder>
</td>
<td-banner>&nbsp;&nbsp;</td>
<td valign=bottom align=right style="position:relative;bottom:0;left:0;">
<table cellpadding=0 cellspacing=0 border=0>
<tr>
<td>
<table height=100% cellpadding=0 cellspacing=0>
<tr>
<td>
<SharePoint:SiteActions runat="server" AccessKey="<%$Resources:wss,tb_SiteActions_AK%>"
PrefixHtml="&lt;div&gt;&lt;div&gt;"
SuffixHtml="&lt;/div&gt;&lt;/div&gt;"
MenuNotVisibleHtml="&amp;nbsp;">
<CustomTemplate>
<SharePoint:FeatureMenuTemplate runat="server"
FeatureScope="Site"
Location="Microsoft.SharePoint.StandardMenu"
GroupId="SiteActions"
UseShortId="true"
>
<SharePoint:MenuItemTemplate runat="server"
Text="<%$Resources:wss,viewlsts_pagetitle_create%>"
Description="<%$Resources:wss,siteactions_createdescription%>"
ImageUrl="/_layouts/images/Actionscreate.gif"
MenuGroupId="100"
Sequence="100"
UseShortId="true"
ClientOnClickNavigateUrl="~site/_layouts/create.aspx"
PermissionsString="ManageLists, ManageSubwebs"
PermissionMode="Any" />
<SharePoint:MenuItemTemplate runat="server"
Text="<%$Resources:wss,siteactions_editpage%>"
Description="<%$Resources:wss,siteactions_editpagedescription%>"
ImageUrl="/_layouts/images/ActionsEditPage.gif"
MenuGroupId="100"
Sequence="200"
ClientOnClickNavigateUrl="javascript:MSOLayout_ChangeLayoutMode(false);"
/>
<SharePoint:MenuItemTemplate runat="server"
Text="<%$Resources:wss,settings_pagetitle%>"
Description="<%$Resources:wss,siteactions_sitesettingsdescription%>"
ImageUrl="/_layouts/images/ActionsSettings.gif"
MenuGroupId="100"
Sequence="300"
UseShortId="true"
ClientOnClickNavigateUrl="~site/_layouts/settings.aspx"
PermissionsString="EnumeratePermissions,ManageWeb,ManageSubwebs,AddAndCustomizePages,ApplyThemeAndBorder,ManageAlerts,ManageLists,ViewUsageData"
PermissionMode="Any" />
</SharePoint:FeatureMenuTemplate>
</CustomTemplate>
</SharePoint:SiteActions>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</asp:ContentPlaceHolder>
</TD>
</TR>
<asp:ContentPlaceHolder runat="server">
<wssuc:DesignModeConsole runat="server"/>
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder runat="server">
<SharePoint:DelegateControl runat="server" ControlId="PublishingConsole"
PrefixHtml="&lt;tr&gt;&lt;td colspan=&quot;4&quot; id=&quot;mpdmconsole&quot; class=&quot;ms-consolemptablerow&quot;&gt;"
SuffixHtml="&lt;/td&gt;&lt;/tr&gt;">
</SharePoint:DelegateControl>
</asp:ContentPlaceHolder>
<TR height="100%"><TD><TABLE width="100%" height="100%" cellspacing="0" cellpadding="0">
<tr>
<td valign="middle" nowrap><div style="height:100%"><asp:ContentPlaceHolder runat="server"/></div></td>
<td>
<asp:ContentPlaceHolder runat="server">
<div><IMG SRC="/_layouts/images/blank.gif" width=1 height=100% alt=""></div>
</asp:ContentPlaceHolder>
</td>
<td valign=top class='ms-pagetitleareaframe' nowrap>
<table cellpadding=0 cellspacing=0 width=100% border="0">
<tr>
<td valign="top">
<asp:ContentPlaceHolder runat="server">
<asp:SiteMapPath SiteMapProvider="SPContentMapProvider" SkipLinkText="" NodeStyle-CssClass="ms-sitemapdirectional" runat="server"/> &nbsp;
</asp:ContentPlaceHolder>
</td>
</tr>
<tr>
<td height=100% valign=top>
<h2>
<asp:ContentPlaceHolder runat="server" />
</h2>
</td>
</tr>
</table>
</td>
<td>
<asp:ContentPlaceHolder runat="server"/>
<asp:ContentPlaceHolder runat="server">
<div style='height:100%'><IMG SRC="/_layouts/images/blank.gif" width=1 height=1 alt=""></div>
</asp:ContentPlaceHolder></td>
</tr>
<asp:ContentPlaceHolder runat="server"/>
<TR>
<TD valign=top height=100%>
<table-nav width=100% height=100% cellpadding=0 cellspacing=0>
<tr>
<td>
<TABLE height="100%"-navframe CELLPADDING=0 CELLSPACING=0 border="0">
<tr valign="top">
<td width="4px"><IMG SRC="/_layouts/images/blank.gif" width=4 height=1 alt=""></td>
<td valign="top" width="100%">
<asp:ContentPlaceHolder runat="server" />
<asp:ContentPlaceHolder runat="server" />
<asp:ContentPlaceHolder runat="server"/>
<asp:ContentPlaceHolder runat="server">
<div>
<div style="width:100%">
<h3><label>
<SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,quiklnch_pagetitle%>" EncodeMethod="HtmlEncode"/></label>
<Sharepoint:SPSecurityTrimmedControl runat="server" PermissionsString="ViewFormPages">
<div><SharePoint:SPLinkButton runat="server" NavigateUrl="~site/_layouts/viewlsts.aspx" Text="<%$Resources:wss,quiklnch_allcontent%>" AccessKey="<%$Resources:wss,quiklnch_allcontent_AK%>"/></div>
</SharePoint:SPSecurityTrimmedControl>
</h3>
<Sharepoint:SPNavigationManager
runat="server"
QuickLaunchControlId="QuickLaunchMenu"
ContainedControl="QuickLaunch"
EnableViewState="false">
<div>
<SharePoint:DelegateControl runat="server"
ControlId="QuickLaunchDataSource">
<Template_Controls>
<asp:SiteMapDataSource
SiteMapProvider="SPNavigationProvider"
ShowStartingNode="False"
StartingNodeUrl="sid:1025"
runat="server"
/>
</Template_Controls>
</SharePoint:DelegateControl>
<SharePoint:AspMenu
DataSourceId="QuickLaunchSiteMap"
runat="server"
Orientation="Vertical"
StaticDisplayLevels="2"
ItemWrap="true"
MaximumDynamicDisplayLevels="0"
StaticSubMenuIndent="0"
SkipLinkText=""
>
<LevelMenuItemStyles>
<asp:MenuItemStyle CssClass="ms-navheader"/>
<asp:MenuItemStyle CssClass="ms-navitem"/>
</LevelMenuItemStyles>
<LevelSubMenuStyles>
<asp:SubMenuStyle CssClass="ms-navSubMenu1"/>
<asp:SubMenuStyle CssClass="ms-navSubMenu2"/>
</LevelSubMenuStyles>
<LevelSelectedStyles>
<asp:MenuItemStyle CssClass="ms-selectednavheader"/>
<asp:MenuItemStyle CssClass="ms-selectednav"/>
</LevelSelectedStyles>
</SharePoint:AspMenu>
</div>
</Sharepoint:SPNavigationManager>
<Sharepoint:SPNavigationManager
runat="server"
ContainedControl="TreeView">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td nowrap>
<SharePoint:SPLinkButton runat="server" NavigateUrl="~site/_layouts/viewlsts.aspx" Text="<%$Resources:wss,treeview_header%>" AccessKey="<%$Resources:wss,quiklnch_allcontent_AK%>"/>
</td>
</tr>
</table>
</td>
</tr>
</table>
<div>
<SharePoint:SPHierarchyDataSourceControl
runat="server"
RootContextObject="Web"
IncludeDiscussionFolders="true"
/>
<SharePoint:SPRememberScroll runat="server" Style="overflow: auto;height: 400px;width: 150px; ">
<Sharepoint:SPTreeView
id="WebTreeView"
runat="server"
ShowLines="false"
DataSourceId="TreeViewDataSource"
ExpandDepth="0"
SelectedNodeStyle-CssClass="ms-tvselected"
NodeStyle-CssClass="ms-navitem"
NodeStyle-HorizontalPadding="2"
SkipLinkText=""
NodeIndent="12"
ExpandImageUrl="/_layouts/images/tvplus.gif"
CollapseImageUrl="/_layouts/images/tvminus.gif"
NoExpandImageUrl="/_layouts/images/tvblank.gif"
>
</Sharepoint:SPTreeView>
</Sharepoint:SPRememberScroll>
</div>
</Sharepoint:SPNavigationManager>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr><td>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr><td nowrap>
<SharePoint:SPLinkButton runat="server" NavigateUrl="~site/_layouts/recyclebin.aspx" ImageUrl="/_layouts/images/recycbin.gif" Text="<%$Resources:wss,StsDefault_RecycleBin%>" PermissionsString="DeleteListItems"/>
</td></tr>
</table>
</td></tr></table>
</div>
</div>
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder runat="server"></asp:ContentPlaceHolder>
</td>
</tr>
<tr><td colspan=2><asp:ContentPlaceHolder runat="server"><IMG SRC="/_layouts/images/blank.gif" width=138 height=1 alt=""></asp:ContentPlaceholder></td></tr>
</TABLE></td><td><asp:ContentPlaceHolder runat="server"></asp:ContentPlaceHolder></td>
</tr></table></TD>
<td><asp:ContentPlaceHolder runat="server"><div><IMG SRC="/_layouts/images/blank.gif" width=10 height=1 alt=""></div></asp:ContentPlaceHolder></td>
<td class='ms-bodyareacell' valign="top">
<table width=100% height="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td class='ms-bodyareaframe' valign="top" height="100%">
<A></A>
<asp:ContentPlaceHolder runat="server" />
<asp:ContentPlaceHolder runat="server" />
</td>
</tr>
</table>
</td>
<td><asp:ContentPlaceHolder runat="server">
<div><IMG SRC="/_layouts/images/blank.gif" width=10 height=1 alt=""></div>
</asp:ContentPlaceHolder></td>
</TR>
<tr>
<td><IMG SRC="/_layouts/images/blank.gif" width=1 height=10 alt=""></td>
<td><IMG SRC="/_layouts/images/blank.gif" width=1 height=10 alt=""></td>
<td><IMG SRC="/_layouts/images/blank.gif" width=1 height=10 alt=""></td>
<td><IMG SRC="/_layouts/images/blank.gif" width=1 height=10 alt=""></td>
</tr>
</TABLE>
<asp:ContentPlaceHolder runat="server" />
<asp:ContentPlaceHolder runat="server" />
</TD></TR>
</asp:ContentPlaceHolder>
<tr >
<td valign="top">
<table width="100%">
<tr>
<td>
<table>
<tr>
<td nowrap="nowrap"><b>YOUR LINKS HERE</b></td>
</tr>
</table>
</td>
<td width="100%" align="center" valign="middle">
</td>
<td valign="top" style="padding-left:3px; padding-right:20px;" nowrap="nowrap">
<b>Readers & Viewers</b><br />
<SharePoint:SPLinkButton Target="_new" ImageUrl="~site/_layouts/images/pdf.gif" runat="server" NavigateUrl="http://get.adobe.com/reader/" Text="PDF" PermissionsString="ViewPages"/>&nbsp;
<SharePoint:SPLinkButton Target="_new" ImageUrl="~site/_layouts/images/ppt.gif" runat="server" NavigateUrl="http://www.microsoft.com/downloads/details.aspx?FamilyId=428D5727-43AB-4F24-90B7-A94784AF71A4&displaylang=en" Text="PowerPoint" PermissionsString="ViewPages"/>&nbsp;
<SharePoint:SPLinkButton Target="_new" ImageUrl="~site/_layouts/images/zip.gif" runat="server" NavigateUrl="http://www.winzip.com/" Text="ZIP" PermissionsString="ViewPages"/>&nbsp;
<SharePoint:SPLinkButton Target="_new" ImageUrl="~site/_layouts/images/doc.gif" runat="server" NavigateUrl="http://www.microsoft.com/downloads/details.aspx?FamilyID=95e24c87-8732-48d5-8689-ab826e7b8fdf" Text="Word" PermissionsString="ViewPages"/>&nbsp;
<SharePoint:SPLinkButton Target="_new" ImageUrl="~site/_layouts/images/xls.gif" runat="server" NavigateUrl="http://www.microsoft.com/downloads/details.aspx?FamilyId=C8378BF4-996C-4569-B547-75EDBD03AAF0" Text="Excel" PermissionsString="ViewPages"/>
</td>
<td valign="top" nowrap="nowrap">
<b>
<SharePoint:SPLinkButton runat="server" NavigateUrl="~site/_layouts/YourPageHere.aspx" Text="Your Site Information" PermissionsString="ViewPages"/></b>
<br/>&nbsp;
</td>
</tr>
</table>
</td>
</tr>
<tr >
<td align="center">
<table style="background-image:url(YOUR-URL-TO-YOUR-WATERMARK.gif);background-position:center; background-repeat:no-repeat">
<tr>
<td align="center" valign="middle" style="padding-left:3px; padding-right:6px;">
YOUR DISCLAIMER TEXT
</td>
</tr>
</table>
</td>
</tr>
</table>
<asp:ContentPlaceHolder runat="server">
<SharePoint:FormDigest runat=server/>
</asp:ContentPlaceHolder>
<input style="display:none;" size=1/>
<input style="display:none;" size=1/>
<asp:ContentPlaceHolder runat="server">
</asp:ContentPlaceHolder>
</form>
</BODY>
</HTML>

CHILD MASTER PAGE

<%@ master masterpagefile="~masterurl/default.master" meta:progid="SharePoint.WebPartPage.Document" %>

<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Register Tagprefix="SPSWC" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="wssuc" TagName="DesignModeConsole" src="~/_controltemplates/DesignModeConsole.ascx" %>
<%@ Register TagPrefix="PublishingVariations" TagName="VariationsLabelMenu" src="~/_controltemplates/VariationsLabelMenu.ascx" %>
<%@ Register Tagprefix="PublishingConsole" TagName="Console" src="~/_controltemplates/PublishingConsole.ascx" %>
<%@ Register TagPrefix="PublishingSiteAction" TagName="SiteActionMenu" src="~/_controltemplates/PublishingActionMenu.ascx" %>
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<asp:ContentPlaceHolder runat="server" />
</asp:Content>
<asp:Content ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
<asp:ContentPlaceHolder runat="server" />
</asp:Content>
<asp:Content runat="server" contentplaceholderid="PlaceHolderMainBase">
<tr><td>
<table width=100% cellpadding=0 cellspacing=0 border=0><tr>
<td>
<SharePoint:SiteLogoImage LogoImageUrl="/_layouts/images/logo.jpg" runat="server"/></td>
<td width=100%>
<asp:ContentPlaceHolder runat="server">
<h1>
<SharePoint:SPLinkButton runat="server" NavigateUrl="~site/">
<SharePoint:ProjectProperty Property="Title" runat="server" />
</SharePoint:SPLinkButton>
</h1>
</asp:ContentPlaceHolder>
</td>
<td style="padding-top:8px;" valign=top>
<asp:ContentPlaceHolder runat="server">
<SharePoint:DelegateControl runat="server" ControlId="SmallSearchInputBox"/>
</asp:ContentPlaceHolder>
</td>
</tr>
</table>
</td>
</tr>
<TR>
<TD WIDTH=100%>
<asp:ContentPlaceHolder runat="server">
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td nowrap valign="middle"></td>
<td-banner width=99% nowrap>
<asp:ContentPlaceHolder runat="server">
<SharePoint:AspMenu
Runat="server"
DataSourceID="topSiteMap"
EnableViewState="false"
AccessKey="<%$Resources:wss,navigation_accesskey%>"
Orientation="Horizontal"
StaticDisplayLevels="2"
MaximumDynamicDisplayLevels="1"
DynamicHorizontalOffset="0"
StaticPopoutImageUrl="/_layouts/images/menudark.gif"
StaticPopoutImageTextFormatString=""
DynamicHoverStyle-BackColor="#CBE3F0"
SkipLinkText=""
StaticSubMenuIndent="0"
CssClass="ms-topNavContainer">
<StaticMenuStyle/>
<StaticMenuItemStyle CssClass="ms-topnav" ItemSpacing="0px"/>
<StaticSelectedStyle CssClass="ms-topnavselected" />
<StaticHoverStyle CssClass="ms-topNavHover" />
<DynamicMenuStyle BackColor="#F2F3F4" BorderColor="#A7B4CE" BorderWidth="1px"/>
<DynamicMenuItemStyle CssClass="ms-topNavFlyOuts"/>
<DynamicHoverStyle CssClass="ms-topNavFlyOutsHover"/>
<DynamicSelectedStyle CssClass="ms-topNavFlyOutsSelected"/>
</SharePoint:AspMenu>
<SharePoint:DelegateControl runat="server" ControlId="TopNavigationDataSource">
<Template_Controls>
<asp:SiteMapDataSource
ShowStartingNode="False"
SiteMapProvider="SPNavigationProvider"
runat="server"
StartingNodeUrl="sid:1002"/>
</Template_Controls>
</SharePoint:DelegateControl>
</asp:ContentPlaceHolder>
</td>
<td-banner>&nbsp;&nbsp;</td>
<td valign=bottom align=right style="position:relative;bottom:0;left:0;">
<table cellpadding=0 cellspacing=0 border=0>
<tr>
<td>
<table height=100% cellpadding=0 cellspacing=0>
<tr>
<td>
<SharePoint:SiteActions runat="server" AccessKey="<%$Resources:wss,tb_SiteActions_AK%>"
PrefixHtml="&lt;div&gt;&lt;div&gt;"
SuffixHtml="&lt;/div&gt;&lt;/div&gt;"
MenuNotVisibleHtml="&amp;nbsp;">
<CustomTemplate>
<SharePoint:FeatureMenuTemplate runat="server"
FeatureScope="Site"
Location="Microsoft.SharePoint.StandardMenu"
GroupId="SiteActions"
UseShortId="true"
>
<SharePoint:MenuItemTemplate runat="server"
Text="<%$Resources:wss,viewlsts_pagetitle_create%>"
Description="<%$Resources:wss,siteactions_createdescription%>"
ImageUrl="/_layouts/images/Actionscreate.gif"
MenuGroupId="100"
Sequence="100"
UseShortId="true"
ClientOnClickNavigateUrl="~site/_layouts/create.aspx"
PermissionsString="ManageLists, ManageSubwebs"
PermissionMode="Any" />
<SharePoint:MenuItemTemplate runat="server"
Text="<%$Resources:wss,siteactions_editpage%>"
Description="<%$Resources:wss,siteactions_editpagedescription%>"
ImageUrl="/_layouts/images/ActionsEditPage.gif"
MenuGroupId="100"
Sequence="200"
ClientOnClickNavigateUrl="javascript:MSOLayout_ChangeLayoutMode(false);"
/>
<SharePoint:MenuItemTemplate runat="server"
Text="<%$Resources:wss,settings_pagetitle%>"
Description="<%$Resources:wss,siteactions_sitesettingsdescription%>"
ImageUrl="/_layouts/images/ActionsSettings.gif"
MenuGroupId="100"
Sequence="300"
UseShortId="true"
ClientOnClickNavigateUrl="~site/_layouts/settings.aspx"
PermissionsString="EnumeratePermissions,ManageWeb,ManageSubwebs,AddAndCustomizePages,ApplyThemeAndBorder,ManageAlerts,ManageLists,ViewUsageData"
PermissionMode="Any" />
</SharePoint:FeatureMenuTemplate>
</CustomTemplate>
</SharePoint:SiteActions>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</asp:ContentPlaceHolder>
</TD>
</TR>
<asp:ContentPlaceHolder runat="server">
<wssuc:DesignModeConsole runat="server"/>
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder runat="server">
<SharePoint:DelegateControl runat="server" ControlId="PublishingConsole"
PrefixHtml="&lt;tr&gt;&lt;td colspan=&quot;4&quot; id=&quot;mpdmconsole&quot; class=&quot;ms-consolemptablerow&quot;&gt;"
SuffixHtml="&lt;/td&gt;&lt;/tr&gt;">
</SharePoint:DelegateControl>
</asp:ContentPlaceHolder>
<TR height="100%"><TD><TABLE width="100%" height="100%" cellspacing="0" cellpadding="0">
<tr>
<td valign="middle" nowrap><div style="height:100%"><asp:ContentPlaceHolder runat="server"/></div></td>
<td>
<asp:ContentPlaceHolder runat="server">
<div><IMG SRC="/_layouts/images/blank.gif" width=1 height=100% alt=""></div>
</asp:ContentPlaceHolder>
</td>
<td valign=top class='ms-pagetitleareaframe' nowrap>
<table cellpadding=0 cellspacing=0 width=100% border="0">
<tr>
<td valign="top">
<asp:ContentPlaceHolder runat="server">
<asp:SiteMapPath SiteMapProvider="SPContentMapProvider" SkipLinkText="" NodeStyle-CssClass="ms-sitemapdirectional" runat="server"/> &nbsp;
</asp:ContentPlaceHolder>
</td>
</tr>
<tr>
<td height=100% valign=top>
<h2>
<asp:ContentPlaceHolder runat="server" />
</h2>
</td>
</tr>
</table>
</td>
<td>
<asp:ContentPlaceHolder runat="server"/>
<asp:ContentPlaceHolder runat="server">
<div style='height:100%'><IMG SRC="/_layouts/images/blank.gif" width=1 height=1 alt=""></div>
</asp:ContentPlaceHolder></td>
</tr>
<asp:ContentPlaceHolder runat="server"/>
<TR>
<TD valign=top height=100%>
<table-nav width=100% height=100% cellpadding=0 cellspacing=0>
<tr>
<td>
<TABLE height="100%"-navframe CELLPADDING=0 CELLSPACING=0 border="0">
<tr valign="top">
<td width="4px"><IMG SRC="/_layouts/images/blank.gif" width=4 height=1 alt=""></td>
<td valign="top" width="100%">
<asp:ContentPlaceHolder runat="server" />
<asp:ContentPlaceHolder runat="server" />
<asp:ContentPlaceHolder runat="server"/>
<asp:ContentPlaceHolder runat="server">
<div>
<div style="width:100%">
<h3><label>
<SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,quiklnch_pagetitle%>" EncodeMethod="HtmlEncode"/></label>
<Sharepoint:SPSecurityTrimmedControl runat="server" PermissionsString="ViewFormPages">
<div><SharePoint:SPLinkButton runat="server" NavigateUrl="~site/_layouts/viewlsts.aspx" Text="<%$Resources:wss,quiklnch_allcontent%>" AccessKey="<%$Resources:wss,quiklnch_allcontent_AK%>"/></div>
</SharePoint:SPSecurityTrimmedControl>
</h3>
<Sharepoint:SPNavigationManager
runat="server"
QuickLaunchControlId="QuickLaunchMenu"
ContainedControl="QuickLaunch"
EnableViewState="false">
<div>
<SharePoint:DelegateControl runat="server"
ControlId="QuickLaunchDataSource">
<Template_Controls>
<asp:SiteMapDataSource
SiteMapProvider="SPNavigationProvider"
ShowStartingNode="False"
StartingNodeUrl="sid:1025"
runat="server"
/>
</Template_Controls>
</SharePoint:DelegateControl>
<SharePoint:AspMenu
DataSourceId="QuickLaunchSiteMap"
runat="server"
Orientation="Vertical"
StaticDisplayLevels="2"
ItemWrap="true"
MaximumDynamicDisplayLevels="0"
StaticSubMenuIndent="0"
SkipLinkText=""
>
<LevelMenuItemStyles>
<asp:MenuItemStyle CssClass="ms-navheader"/>
<asp:MenuItemStyle CssClass="ms-navitem"/>
</LevelMenuItemStyles>
<LevelSubMenuStyles>
<asp:SubMenuStyle CssClass="ms-navSubMenu1"/>
<asp:SubMenuStyle CssClass="ms-navSubMenu2"/>
</LevelSubMenuStyles>
<LevelSelectedStyles>
<asp:MenuItemStyle CssClass="ms-selectednavheader"/>
<asp:MenuItemStyle CssClass="ms-selectednav"/>
</LevelSelectedStyles>
</SharePoint:AspMenu>
</div>
</Sharepoint:SPNavigationManager>
<Sharepoint:SPNavigationManager
runat="server"
ContainedControl="TreeView">
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td nowrap>
<SharePoint:SPLinkButton runat="server" NavigateUrl="~site/_layouts/viewlsts.aspx" Text="<%$Resources:wss,treeview_header%>" AccessKey="<%$Resources:wss,quiklnch_allcontent_AK%>"/>
</td>
</tr>
</table>
</td>
</tr>
</table>
<div>
<SharePoint:SPHierarchyDataSourceControl
runat="server"
RootContextObject="Web"
IncludeDiscussionFolders="true"
/>
<SharePoint:SPRememberScroll runat="server" Style="overflow: auto;height: 400px;width: 150px; ">
<Sharepoint:SPTreeView
runat="server"
ShowLines="false"
DataSourceId="TreeViewDataSource"
ExpandDepth="0"
SelectedNodeStyle-CssClass="ms-tvselected"
NodeStyle-CssClass="ms-navitem"
NodeStyle-HorizontalPadding="2"
SkipLinkText=""
NodeIndent="12"
ExpandImageUrl="/_layouts/images/tvplus.gif"
CollapseImageUrl="/_layouts/images/tvminus.gif"
NoExpandImageUrl="/_layouts/images/tvblank.gif"
>
</Sharepoint:SPTreeView>
</Sharepoint:SPRememberScroll>
</div>
</Sharepoint:SPNavigationManager>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr><td>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr><td nowrap>
<SharePoint:SPLinkButton runat="server" NavigateUrl="~site/_layouts/recyclebin.aspx" ImageUrl="/_layouts/images/recycbin.gif" Text="<%$Resources:wss,StsDefault_RecycleBin%>" PermissionsString="DeleteListItems"/>
</td></tr>
</table>
</td></tr></table>
</div>
</div>
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder runat="server"></asp:ContentPlaceHolder>
</td>
</tr>
<tr><td colspan=2><asp:ContentPlaceHolder runat="server"><IMG SRC="/_layouts/images/blank.gif" width=138 height=1 alt=""></asp:ContentPlaceholder></td></tr>
</TABLE></td><td><asp:ContentPlaceHolder runat="server"></asp:ContentPlaceHolder></td>
</tr></table></TD>
<td><asp:ContentPlaceHolder runat="server"><div><IMG SRC="/_layouts/images/blank.gif" width=10 height=1 alt=""></div></asp:ContentPlaceHolder></td>
<td class='ms-bodyareacell' valign="top">
<table width=100% height="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td class='ms-bodyareaframe' valign="top" height="100%">
<A></A>
<asp:ContentPlaceHolder runat="server" />
<asp:ContentPlaceHolder runat="server" />
</td>
</tr>
</table>
</td>
<td><asp:ContentPlaceHolder runat="server">
<div><IMG SRC="/_layouts/images/blank.gif" width=10 height=1 alt=""></div>
</asp:ContentPlaceHolder></td>
</TR>
<tr>
<td><IMG SRC="/_layouts/images/blank.gif" width=1 height=10 alt=""></td>
<td><IMG SRC="/_layouts/images/blank.gif" width=1 height=10 alt=""></td>
<td><IMG SRC="/_layouts/images/blank.gif" width=1 height=10 alt=""></td>
<td><IMG SRC="/_layouts/images/blank.gif" width=1 height=10 alt=""></td>
</tr>
</TABLE>
<asp:ContentPlaceHolder runat="server" />
<asp:ContentPlaceHolder runat="server" />
</TD></TR>
</asp:Content>

customURLtoUseCHILD = web.ServerRelativeUrl + customizedMasterUrlCHILD;
customURLtoUseCHILD = customURLtoUseCHILD.Replace(“//”, “/”);
// Store the old Master URL’s and Custom Master URL’s
web.AllProperties[“OldMasterUrl”] = web.MasterUrl;
web.AllProperties[“OldCustomMasterUrl”] = web.CustomMasterUrl;
// Assign the Master URL to both properties
web.MasterUrl = customURLtoUsePARENT;
web.CustomMasterUrl = customURLtoUseCHILD;
// Update the Web
web.Update();
}
}
catch { }
}
You may also wish to remove BOTH files in the FeatureDeactivating function.
Finally, instead of stapling the feature to GLOBAL, we need to specify the templates. Namely, all of them except for the Meeting Workspaces templates. This is due to a SharePoint bug where if you create a meeting workspace from a recurring Calendar event, if you try to staple a feature to the new meeting workspace site, it breaks the scripting on the page such that you cannot switch dates.
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#2">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="WIKI#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLOG#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BDR#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="EAWF#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="OFFILE#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="OFFILE#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PWA#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PWS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSMSITE#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSTOC#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSTOPIC#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSNEWS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSNHOME#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSSITES#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSBWEB#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSCOMMU#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSREPORTCENTER#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSPORTAL#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SRCHCEN#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PROFILES#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="CMSPUBLISHING#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#2">
</elements>

 

customURLtoUsePARENT = web.ServerRelativeUrl + customizedMasterUrlPARENT;
customURLtoUsePARENT = customURLtoUsePARENT.Replace(“//”, “/”);
// Store the old Master URL’s and Custom Master URL’s
web.AllProperties[“OldMasterUrl”] = web.MasterUrl;
web.AllProperties[“OldCustomMasterUrl”] = web.CustomMasterUrl;
// Assign the Master URL to both properties
web.MasterUrl = customURLtoUsePARENT;
web.CustomMasterUrl = customURLtoUseCHILD;
// Update the Web
web.Update();
}
}
catch { }
}
You may also wish to remove BOTH files in the FeatureDeactivating function.
Finally, instead of stapling the feature to GLOBAL, we need to specify the templates. Namely, all of them except for the Meeting Workspaces templates. This is due to a SharePoint bug where if you create a meeting workspace from a recurring Calendar event, if you try to staple a feature to the new meeting workspace site, it breaks the scripting on the page such that you cannot switch dates.
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#2">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="WIKI#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLOG#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BDR#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="EAWF#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="OFFILE#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="OFFILE#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PWA#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PWS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSMSITE#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSTOC#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSTOPIC#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSNEWS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSNHOME#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSSITES#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSBWEB#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSCOMMU#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSREPORTCENTER#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSPORTAL#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SRCHCEN#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PROFILES#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="CMSPUBLISHING#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#2">
</elements>

 

MOSS – Global Custom Master Template

Objective

The Problem

Let’s say you want to force your entire organization to use your custom master page. You may wish to have certain contact information, legal disclaimers, utilities, or other functionality on your page that is standard company-wide. The problem we face is how to get this feature automatically activated on each and every site, of every type, when newly created.

The Solution

We will create 2 features – one to install the Custom Master Page into the Master Page Gallery of each site, as well as activate that page as the currently used master page; and one feature, called “Feature Stapling”, to have that first feature automatically activate.

References

The code in this article is taken in large part from other articles out there in blogLand – PLEASE at least skim over these articles to see the similarities and differences to this post. Also, there is a lot more explanation of the code itself (what it does) in Becky Bertram’s article.

Why is this Different from the first referenced article?

It isn’t all that much different from Becky Bertram’s code – let me please give credit where due. However, I found that in some circumstances the URL to the custom master page did not work; and most importantly, this only worked for Sub Sites already added to the Site Collection. I wanted to get a feature which activated on each and every NEW site and site collection. As for the existing sites, well, we’ll just use CorasWorks Design Migrator to push out the master page to those sites.

Alternatively, once the feature is installed, you can activate it on every existing site. If you don’t want to do this on every site, and just want to do this for a site collection down, then take Becky Bertram’s original code with the ProcessSubWebs method which pushes the changes to every child.

Finally, I wanted to write an article that not only had the code, but also the complete steps to create the solution package, which I find is not all that intuitive if you’re not used to it. For the second feature, we won’t even bother to create a solution – we’ll just use the elements and feature files in a folder and run an stsadm command line on them.

Danger

A word of warning – test your master page thoroughly before turning on these features. You could potentially break every page in your site if you’re not careful. Always keep the url /_layouts/settings.aspx in handy reach in case you get an error on the page and need to go back to turn the feature off. Sometimes you will get a “File Not Found” or other error if the custom URL path is off, and you can’t even navigate around the regular pages of the site.

Also, NEVER MESS WITH default.master IN THE 12 HIVE GLOBAL DIRECTORY.  Service packs will overwrite your changes.

FEATURE # 1 – Master Page with Feature Receiver

Install WSPBuilder
Create the Visual Studio Project
  • File à New à Project
  • WSPBuilder à WSPBuilder Project
  • Under the 12 folder, create a folder named Template
  • Under the Template folder, create a folder named Features
  • Under the Features folder, create a folder named YourCompanyCustomMaster
  • Under the YourCompanyCustomMaster folder, create a folder named MasterPages
  • Under the YourCompanyCustomMaster folder, create a file named feature.xml
  • Under the YourCompanyCustomMaster folder, create a file named elements.xml
  • Under the MasterPages folder, create a master page file named YourCompanyCustomMaster.master
  • Under the root of your project, create a code file named FeatureReceiver.cs.
  • Your project should now resemble the following:
     

    • File Structure

      File Structure

      Paste the code from below into the Feature.xml File, the Elements.xml file, and the FeatureReceiver.cs file.

    • Create a new Guid for your Feature ID (In Visual Studio, choose Tools à Create GUID) and paste it in to the Feature ID. Be sure to remove any { } braces from the guid, such that the end result resembles: Id=F647BBF6-5277-4118-9FA8-87D3E7C2059C“.  NOTE: The ID you create here is the ID you will use in the elements file in the second feature.
    • In order to get a Public Key for the Receiver Assembly, temporarily register this file into your GAC. I find the easiest way to do this is to open up C:\Windows\Assembly and just drag the file in. Then right-click on the file and choose Properties. You can copy the public key token right from the message box. The project has to have a key file associated with it to be registered into the GAC, but WSPBuilder already did that for you.
       

      Assembly Key

      Assembly Key

    • Compile your code. Right-click on the project and choose WSPBuilder à Build WSP. This builds the WSP file and places it in the root directory.
       

      Build WSP

      Build WSP

    • If you are developing on your Development SharePoint machine, or your Production Machine (although I would NOT recommend deploying this straight into production), you can use the WSPBuilder command “Deploy” (see above picture) to move the WSP file into the SharePoint environment. If you do not have WSPBuilder, or you are not on your SharePoint box, then copy the WSP file over to the SharePoint machine and run the following commands from STSADM (Typically located in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN)
          stsadm -o addsolution -filename <file_path>\YourCompanyCustomMaster.wsp, where <file_path> is the location of your wsp file
          stsadm -o deploysolution -name YourCompanyCustomMaster.wsp -allowgacdeployment
Feature File

<Feature Id=<a new guid> Title=YourCompany Custom Master Page
   Scope=Web Version=1.0.0.0 Hidden=FALSE DefaultResourceFile=core
   xmlns=http://schemas.microsoft.com/sharepoint/
   Description
=This Feature contains the YourCompany’s Custom Master Page
   ReceiverAssembly=YourCompanyCustomMaster, Version=1.0.0.0, Culture=neutral,     
      PublicKeyToken=<your public key>

   ReceiverClass=YourCompanyCustomMaster.FeatureReceiver>
   <ElementManifests>
      <ElementManifest Location=elements.xml />
      <ElementFile Location=MasterPages\YourCompanyCustomMaster.master />
   </ElementManifests>
</Feature>

Elements File

<Elements xmlns=http://schemas.microsoft.com/sharepoint/>
   <Module Name=YourCompanyCustomMaster Url=_catalogs/masterpage Path=MasterPages
      RootWebOnly
=TRUE

      <File Url=YourCompanyCustomMaster.master Name=YourCompanyCustomMaster.master
         Type
=GhostableInLibrary >

         <Property Name=ContentType
            Value
=$Resources:cmscore,contenttype_masterpage_name; />

      </File>
   </Module>
</Elements>  

Feature Receiver

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
 
namespace YourCompanyCustomMaster
{
   public class FeatureReceiver : SPFeatureReceiver
   {
      // Code adapted from:
      // http://www.beckybertram.com/oldblog/index.php?p=33&more=1&c=1&tb=1&pb=1
 
      // Master Page Feature adapted from:
      // http://sharepointmagazine.net/technical/development/deploying-the-master-page
 
      const string defaultMasterUrl = “/_catalogs/masterpage/default.master”;
      const string customizedMasterUrl = “/_catalogs/masterpage/AKGCustomMaster.master”;
 
      public override void FeatureInstalled(SPFeatureReceiverProperties properties) { }
 
      public override void FeatureUninstalling(SPFeatureReceiverProperties properties) { }
 
      public override void FeatureActivated(SPFeatureReceiverProperties properties)
      {
         try
         {
            using (SPWeb web = (SPWeb)properties.Feature.Parent)
            {
               string customURLtoUse = customizedMasterUrl;
 
               customURLtoUse = web.ServerRelativeUrl + customizedMasterUrl;
               customURLtoUse = customURLtoUse.Replace(“//”, “/”);
 
               // Store the old Master URL’s and Custom Master URL’s
               web.AllProperties[“OldMasterUrl”] = web.MasterUrl; 
               web.AllProperties[“OldCustomMasterUrl”] = web.CustomMasterUrl; 
 
               // Assign the Master URL to both properties
               web.MasterUrl = customURLtoUse;
               web.CustomMasterUrl = customURLtoUse;
 
               // Update the Web
               web.Update(); 
            } 
         }
         catch { } 
      }
 
      private void DeactivateWeb(SPWeb web)
      {
         try
         {
            if (web.AllProperties.ContainsKey(“OldMasterUrl”))
            {
               // Change the MasterURL and CustomMasterURL back to
               // old versions, if the property exists
               string oldMasterUrl = web.AllProperties[“OldMasterUrl”].ToString();
               try
               { 
                  bool fileExists = web.GetFile(oldMasterUrl).Exists;
                  web.MasterUrl = oldMasterUrl;
               }
               catch (ArgumentException)
               {
                  web.MasterUrl = defaultMasterUrl;
               }
 
               string oldCustomUrl = web.AllProperties[“OldCustomMasterUrl”].ToString();
               try
               {
                  bool fileExists = web.GetFile(oldCustomUrl).Exists;
                  web.CustomMasterUrl = web.AllProperties[“OldCustomMasterUrl”].ToString();
               }
               catch (ArgumentException)
               {
                  web.CustomMasterUrl = defaultMasterUrl;
               }
 
               // Remove the custom properties
               web.AllProperties.Remove(“OldMasterUrl”);
               web.AllProperties.Remove(“OldCustomMasterUrl”); 
            } 
            else
            {
               // Otherwise, change back to default
               web.MasterUrl = defaultMasterUrl;
               web.CustomMasterUrl = defaultMasterUrl;
            } 
         }
         catch
         {
            try
            {
               web.MasterUrl = defaultMasterUrl;
               web.CustomMasterUrl = defaultMasterUrl;
            } 
            catch { }
         }
      }
 
      public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
      {
         try
         {
            using (SPWeb web = (SPWeb)properties.Feature.Parent)
            {
               DeactivateWeb(web);
               web.Update(); 
 
               string customURLtoUse = web.ServerRelativeUrl + customizedMasterUrl;
               customURLtoUse = customURLtoUse.Replace(“//”, “/”);
 
               // Delete the file manually from the master page gallery
               if (web.MasterUrl != customURLtoUse)
               {
                  try
                  {
                     bool fileExists = web.GetFile(customURLtoUse).Exists;
                     SPFile file = web.GetFile(customURLtoUse);
                     SPFolder masterPageGallery = file.ParentFolder; 
  
                     // Unfortunately, there seems to be an issue in SharePoint where 
                     // you can not delete a master page that was installed by a
                     // feature, even if no one is referencing that master page.
                     // You have to move it to another folder then delete that folder. 
                     SPFolder temp = masterPageGallery.SubFolders.Add(“Temp”);
                     file.MoveTo(temp.Url + “/” + file.Name);
                     temp.Delete(); 
                  }
                  catch (ArgumentException)
                  {
                     return;
                  } 
               } 
            } 
         }
         catch { } 
      }
   }
}
 

FEATURE # 2 – Stapling the first feature to every new web

What is Feature Stapling?

A basic explanation of feature stapling is that it is used to attach one or more features to a SharePoint site definition without modifying an existing site definition or creating a new site definition. For a more detailed explanation I recommend reading Chris Jonson’s blog on “Feature Stapling in WSS V3”.

Create the files in the Feature folder

For this exercise, we will not even bother creating a solution through Visual Studio. Let’s just put the simple files directly into a Feature Folder on the 12 hive and add the solution.

  • Navigate to the 12 hive, usually at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\FEATURES.
  • Create a new folder, and name it YourCompanyMasterStapling.
  • In the YourCompanyMasterStapling folder, create 2 files – elements.xml and feature.xml.
  • Paste the code from below into the Feature.xml File, and the Elements.xml file.
  • Create a new Guid for your Feature ID (In Visual Studio, choose Tools à Create GUID) and paste it in to the Feature ID. Be sure to remove any { } braces from the guid, such that the end result resembles: Id=F647BBF6-5277-4118-9FA8-87D3E7C2059D“.  NOTE: The ID you create here is NOT the ID you’re using in the Elements file – rather use the Feature ID from the FIRST feature.
  • Run the following commands from STSADM (Typically located in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\BIN)
        stsadm -o installfeature -filename YourCompanyMasterStapling\feature.xml
        stsadm -o activatefeature -filename YourCompanyMasterStapling\feature.xml
  • NOTE: You may receive a message stating that the feature has already been activated to the farm.
Feature File

<?xml version=1.0 encoding=utf-8 ?>
<Feature Id=<a new guid>
   Title=YourCompany Custom Master Page Feature Stapling
   Description=YourCompany Custom Master Page Feature Stapling – activates the YourCompany
      Master Page feature for all new sites

   Version=1.0.0.0
   Scope=Farm
   Hidden=TRUE
   xmlns=http://schemas.microsoft.com/sharepoint/
   <ElementManifests>
      <ElementManifest Location=elements.xml/>
   </ElementManifests
</Feature>

Elements File

<Elements xmlns=http://schemas.microsoft.com/sharepoint/>
    <FeatureSiteTemplateAssociation Id=<the guid from feature 1> TemplateName=GLOBAL /> ** Read update below
</Elements>

UPDATE:  Do not use GLOBAL – there is a bug:   if you create a meeting workspace from a recurring Calendar event, if you try to staple a feature to the new meeting workspace site, it breaks the scripting on the page such that you cannot switch dates.  Instead staple to all templates EXCEPT Meeting Workspaces:

<elements xmlns="http://schemas.microsoft.com/sharepoint/">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="STS#2">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="WIKI#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLOG#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BDR#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="EAWF#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="OFFILE#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="OFFILE#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PWA#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PWS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSMSITE#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSTOC#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSTOPIC#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSNEWS#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSNHOME#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSSITES#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSBWEB#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSCOMMU#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSREPORTCENTER#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SPSPORTAL#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="SRCHCEN#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="PROFILES#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="CMSPUBLISHING#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#0">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#1">
<featuresitetemplateassociation id="F647BBF6-5277-4118-9FA8-87D3E7C2059C" templatename="BLANKINTERNET#2">
</elements>

 

Testing

Make sure you test your creation thoroughly. Test the following areas:

Root Web Site

The Root Web of the Root site – typically the portal site (the one without “sites” in the url).

Site Collections

Create a new Site Collection and check its Root Web.

Sub Sites

Create and test each type of Sub Site – create a Team Site, a Blog Site, a Meeting Workspace, a Publishing Portal, and a Document Center Portal at the very least