SharePoint Automation Gary Lapointe – Founding Partner, Aptillon, Inc.

13Aug/0719

Set Available Site Templates

One of the common tasks that administrators will need to perform across various site collections upon initial deployment is to set which site templates (and page layouts) are available for creating subsites and pages. You can do this via the Site Collection Site Settings page (Site Settings -> Page Layout and Site Template Settings). If you need to script it like I did then you'll need to get a PublishingWeb object (via the static PublishingWeb.GetPublishingWeb() method which takes an SPWeb object as an argument) and then call the SetAvailableCrossLanguageWebTemplates() method and the SetAvailableWebTemplates() depending on whether you are setting a cross language (language neutral) template or a language specific template.

Enumerating the list of templates is just as easy - you simply call the GetAvailableWebTemplates() method or the GetAvailableCrossLanguageWebTemplates() method. One of the challenges I had was in determining exactly how Microsoft handles the setting of templates as it's not entirely as obvious as I indicate above due to the fact that a template name can be associated as a cross language template or a language specific template and it's not always clear how it's currently set. I did a lot of digging through Microsoft's code and I believe that I've got it all figured out.

The commands below allow you to enumerate the list of current available and installed templates (helpful for getting the real template names so that you can then call the add and remove commands) as well as add and remove available templates.

1. gl-enuminstalledsitetemplates

The code to enumerate the list of installed templates is extremely simple. We create a new SPSite object passing in the URL of the Site Collection and then we use an SPWeb object to get the list of installed languages which we will iterate through. Then, for each installed language we get the list of web templates and custom web templates by calling the SPSite objects GetWebTemplates() and GetCustomWebTemplates() methods, passing in the locale ID (LCID) we retrieved from the SPWeb.RegionalSettings.InstaleldLanguages.SPLanguage object:

   1: string url = keyValues["url"];
   2:  
   3: using (SPSite site = new SPSite(url))
   4: {
   5:     using (SPWeb web = site.OpenWeb())
   6:     {
   7:  
   8:         foreach (SPLanguage lang in web.RegionalSettings.InstalledLanguages)
   9:         {
  10:             foreach (SPWebTemplate template in site.GetWebTemplates((uint)lang.LCID))
  11:             {
  12:                 output += template.Name + " = " + template.Title + " (" + lang.LCID + ")\r\n";
  13:             }
  14:             foreach (SPWebTemplate template in site.GetCustomWebTemplates((uint)lang.LCID))
  15:             {
  16:                 output += template.Name + " = " + template.Title + " (Custom)(" + lang.LCID + ")\r\n";
  17:             }
  18:         }
  19:     }
  20: }

The syntax of the resultant command can be seen below:

C:\>stsadm -help gl-enuminstalledsitetemplates

stsadm -o gl-enuminstalledsitetemplates

Returns the list of site templates installed for the given site collection.

Parameters:
        -url <site collection url>

Here’s an example of how to enumerate the list of installed templates for a site collection:

stsadm –o gl-enuminstalledsitetemplates –url "http://intranet/"

The results of executing this command can be seen below (your results may be different):

GLOBAL#0 = Global template (1033)
STS#0 = Team Site (1033)
STS#1 = Blank Site (1033)
STS#2 = Document Workspace (1033)
MPS#0 = Basic Meeting Workspace (1033)
MPS#1 = Blank Meeting Workspace (1033)
MPS#2 = Decision Meeting Workspace (1033)
MPS#3 = Social Meeting Workspace (1033)
MPS#4 = Multipage Meeting Workspace (1033)
CENTRALADMIN#0 = Central Admin Site (1033)
WIKI#0 = Wiki Site (1033)
BLOG#0 = Blog (1033)
BDR#0 = Document Center (1033)
OFFILE#0 = Records Center (1033)
OFFILE#1 = Records Center (1033)
OSRV#0 = Shared Services Administration Site (1033)
SPS#0 = SharePoint Portal Server Site (1033)
SPSPERS#0 = SharePoint Portal Server Personal Space (1033)
SPSMSITE#0 = Personalization Site (1033)
SPSTOC#0 = Contents area Template (1033)
SPSTOPIC#0 = Topic area template (1033)
SPSNEWS#0 = News Site (1033)
CMSPUBLISHING#0 = Publishing Site (1033)
BLANKINTERNET#0 = Publishing Site (1033)
BLANKINTERNET#1 = Press Releases Site (1033)
BLANKINTERNET#2 = Publishing Site with Workflow (1033)
SPSNHOME#0 = News Site (1033)
SPSSITES#0 = Site Directory (1033)
SPSCOMMU#0 = Community area template (1033)
SPSREPORTCENTER#0 = Report Center (1033)
SPSPORTAL#0 = Collaboration Portal (1033)
SRCHCEN#0 = Search Center with Tabs (1033)
PROFILES#0 = Profiles (1033)
BLANKINTERNETCONTAINER#0 = Publishing Portal (1033)
SPSMSITEHOST#0 = My Site Host (1033)
SRCHCENTERLITE#0 = Search Center (1033)
SRCHCENTERLITE#1 = Search Center (1033)
SPSBWEB#0 = SharePoint Portal Server BucketWeb Template (1033)

2. gl-enumavailablesitetemplates

The code to enumerate the list of available templates is almost identical to that needed to enumerate installed templates. The only real difference is that instead of getting the collection from the SPSite object you'll be getting it from a PublishingWeb object (not that there is no way to get a list of cross languange installed templates - this one of those things that threw me off - when you install the template it appears to be language specific but you can then make it available as a cross language template):

   1: string url = keyValues["url"];
   2: using (SPSite site = new SPSite(url))
   3: {
   4:     using (SPWeb web = site.OpenWeb())
   5:     {
   6:         PublishingWeb pubweb = PublishingWeb.GetPublishingWeb(web);
   7:  
   8:         //System.Diagnostics.Debugger.Launch();
   9:         foreach (SPWebTemplate template in pubweb.GetAvailableCrossLanguageWebTemplates())
  10:         {
  11:             output += template.Name + " = " + template.Title + " (All)\r\n";
  12:         }
  13:         foreach (SPLanguage lang in web.RegionalSettings.InstalledLanguages)
  14:         {
  15:             foreach (SPWebTemplate template in pubweb.GetAvailableWebTemplates((uint)lang.LCID))
  16:             {
  17:                 output += template.Name + " = " + template.Title + " (" + lang.LCID + ")\r\n";
  18:             }
  19:         }
  20:     }
  21: }

The syntax of the resultant command can be seen below:

C:\>stsadm -help gl-enumavailablesitetemplates

stsadm -o gl-enumavailablesitetemplates

Returns the list of site templates available for the given site collection.

Parameters:
        -url <site collection url>

Here’s an example of how to enumerate the list of available templates for a site collection:

stsadm –o gl-enumavailablesitetemplates –url "http://intranet/"

The results of executing this command can be seen below (your results may be different):

WIKI#0 = Wiki Site (All)
BLANKINTERNET#2 = Publishing Site with Workflow (All)
SPSREPORTCENTER#0 = Report Center (All)
SPSNHOME#0 = News Site (All)
CMSPUBLISHING#0 = Publishing Site (All)
BDR#0 = Document Center (All)
SPSSITES#0 = Site Directory (All)
STS#1 = Blank Site (All)
STS#0 = Team Site (All)
SRCHCEN#0 = Search Center with Tabs (All)
WIKI#0 = Wiki Site (1033)
BLANKINTERNET#2 = Publishing Site with Workflow (1033)
SPSREPORTCENTER#0 = Report Center (1033)
SPSNHOME#0 = News Site (1033)
CMSPUBLISHING#0 = Publishing Site (1033)
BDR#0 = Document Center (1033)
SPSSITES#0 = Site Directory (1033)
STS#1 = Blank Site (1033)
STS#0 = Team Site (1033)
SRCHCEN#0 = Search Center with Tabs (1033)

Note that the reason why some sites appear to be listed twice but only show up once in the web tool is because behind the scenes Microsoft is removing the duplicate (it knows that WIKI#0 is listed as a cross language template and it's locale is 1033 so don't show the locale specific entry if we're already showing as a language neutral template. I chose to not do this filtering so you could see everything that's going on - it wouldn't take much effort to change the code so that it list each item on one line and merely flags the fact that it's listed as cross language and what the specific locale is.

3. gl-addavailablesitetemplate

To add to the list of available templates we simply get a collection of currently available templates and then add to that collection. We then call SetAvailableCrossLanguageWebTemplates() or SetAvailableWebTemplates() passing in the modified collection. The method you call depends on whether the template is a language neutral template or not. The syntax of the resultant command can be seen below:

C:\>stsadm -help gl-addavailablesitetemplate

stsadm -o gl-addavailablesitetemplate

Adds a site template to the list of available templates for the given site collection.

Parameters:
        -url <site collection url>
        -template <template name>
        [-lcid <locale id>]
        [-resetallsubsites]

Here’s an example of how to add a template to the list of available templates for a site collection:

stsadm –o gl-addavailablesitetemplate –url "http://intranet/" -template "WIKI#0" -lcid 1033 -resetallsubsites

4. gl-removeavailablesitetemplate

Removing a template is similar to adding a template but instead of adding to the SPWebTemplateCollection object you're going to remove from it. The syntax of the resultant command can be seen below:

C:\>stsadm -help gl-removeavailablesitetemplate

stsadm -o gl-removeavailablesitetemplate

Removes a site template from the list of available templates for the given site collection.

Parameters:
        -url <site collection url>
        -template <template name>
        [-lcid <locale id>]
        [-resetallsubsites]

Here’s an example of how to remove a template from the list of available templates for a site collection:

stsadm –o gl-removeavailablesitetemplate –url "http://intranet/" -template "WIKI#0" -lcid 1033 -resetallsubsites

9Aug/075

Quota Templates

The following is a list of quota related commands that I have created and are available for download from this site.

1. gl-createquotatemplate

Creating a quota template can be done using the central administration tool by going to “Central Administration > Application Management > Quota Templates”. This command will create a quota template in the same way as is done via the central administration tool.

C:\>stsadm -help gl-createquotatemplate

stsadm -o gl-createquotatemplate

Creates a new quota template

Parameters:
        -name <quota>
        [-storagemaxlevel <maximum>]
        [-storagewarninglevel <warning>]

Here’s an example of how to create a template:

stsadm –o gl-createquotatemplate –name “My Sites” –storagemaxlevel 50 -storagewarninglevel 40

Once the template is created you can assign it to a site using STSADM’s setproperty command:

stsadm –o setproperty –propertyname defaultquotatemplate –propertyvalue “My Sites” –url “http://mysites/”

The main code that handles the creation of the quota template can be seen below.

   1: SPFarm farm = SPFarm.Local;
   2: SPWebService webService = farm.Services.GetValue("");
   3:  
   4: SPQuotaTemplateCollection quotaColl = webService.QuotaTemplates;
   5:  
   6: if (quotaColl[name] != null)
   7: {
   8:  output = "The template specified already exists.";
   9:  return 0;
  10: }
  11: SPQuotaTemplate newTemplate = new SPQuotaTemplate();
  12:  
  13: newTemplate.Name = name;
  14: newTemplate.StorageMaximumLevel = storagemaxlevel;
  15: newTemplate.StorageWarningLevel = storagewarninglevel;
  16:  
  17: quotaColl.Add(newTemplate);

The code is essentially just grabbing an SPWebService class which returns the SPQuotaTemplateCollection. We then use that collection to add new items to the collection - the same is true for editing but instead of adding a new item we are retrieving and modifying an existing item.

2. gl-editquotatemplate

Editing a quota template can be done using the central administration tool by going to “Central Administration > Application Management > Quota Templates”. This command will edit a quota template in the same way as is done via the central administration tool.

C:\>stsadm -help gl-editquotatemplate

stsadm -o gl-editquotatemplate

Edits an existing quota template

Parameters:
        -name <quota name>
        [-storagemaxlevel <maximum storage level in megabytes - set to zero to clear>]
        [-storagewarninglevel <warning level in megabytes - set to zero to clear>]

Here’s an example of how to edit a template:

stsadm –o gl-editquotatemplate –name “My Sites” –storagemaxlevel 50 -storagewarninglevel 40

9Aug/074

Getting Started with STSADM Extensions

Before I talk about the commands that I've created I wanted to spend a moment to show how I've approached the creation of the commands. If you're faced with a situation like mine where you have to automate numerous MOSS configurations then you basically have two options - the first is to use STSADM (and possibly custom extensions) and the third is to just write your own console or WinForm app and plug away against the API.

The reason I chose to use STSADM and custom extensions was so that I wouldn't have to recompile every time I needed to add an additional step and so that I could re-use the commands after we roll out. To get started creating your own extensions the first thing you'll need is a SharePoint environment in which you can test your code. If you use Windows Server 2003 as your development platform then you can simply install and configure SharePoint locally. Otherwise you'll have to do what I did which is to create a virtual PC. My virtual PC has Windows Server 2003 R2 with Reflector, Office 2007, Visual Studio 2005, SQL Server 2005, and of course SharePoint 2007.

I also have another virtual PC which I use to test the gradual upgrade - so my dev VPC is for where I can create, test, break, fix, etc. - once I'm comfortable I move my built code to my upgrade test VPC where I'm scripting out every change using a simple batch file with calls to STSADM. Once your environment is setup the first thing you need to do before creating an extension is make sure that what you need to accomplish can't already be done with the out-of-the-box STSADM.

This may sound obvious but it's more challenging than you may thing - some of the commands are not exactly named clearly and it's easy to miss one when you're scanning through them (especially if you've installed other add-ons like Project Server which adds it's own set of extensions). More than once I've found myself beginning the process of creating a command that I though didn't exist only to discover it was already there later when looking for something else. Once I know I'm stuck creating my own then I would open SharePoint to the page which would allow me to change the configuration via the web interface. For example, the first command I created was one which would allow me to create a quota template (STSADM allows you to set a default quota template to a web application but does not allow you to create one).

So I went to the Quota Templates page in Central Admin (http://yourcaserver/_admin/ManageQuotaTemplate.aspx) to see what kind of information the web site was asking for. The next thing you need to do is either dig through the SDK and try to find the right object to accomplish the task (good luck with that) or see how Microsoft is doing it by disassembling the web page. To disassemble the web page you can either go to the 12 hive where the pages are stored (C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\ADMIN for CA pages), find the right aspx file and look at the Inherits attribute of the @Page tag or you can find the JIT compiled page in your temp folder (C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\) and then drag the dll into Reflector and navigate to the base class.

In either case where you want to get is the base class which in this case is "Microsoft.SharePoint.ApplicationPages.ManageQuotaTemplatePage". You can find the assembly for this class here: C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG\ADMINBIN\Microsoft.SharePoint.ApplicationPages.Administration.dll. Some of the assemblies you'll end up needing are also in the ISAPI folder or the BIN folder (you'll just have to search - some are also only in the GAC so you'll have to go to the command line and manually copy them out to a temp folder so you can disassemble them). Once I've got the page in Reflector then it becomes a simple matter to dig through the code to find what Microsoft is doing.

You'll find in many cases Microsoft is using internal helper classes to do the work so you may have to dig through several levels to figure out how you can do it using classes available to you. In this case you can see fairly quickly that they are using the SPQuotaTemplate class and the SPQuotaTemplateCollection class to manage quotas (this isn't the best example as a quick search in the SDK for "quota" would reveal this information but it demonstrates what you'll have to do most of the time and also allows you to see how Microsoft is handling things such as translation from megabytes to bytes and what other error handling may be involved, among other things).

Now that you have this information you can start creating your extension. My follow on posts will discuss each of the commands I've created, why I needed them, and what some of the challenges were in creating them. For more information about creating the XML file and the project structure see Joerg's posting on Code Project or Tony's article on Zimbio.