Programmatically Setting SharePoint 2010 Calendar Overlays
I recently did a project where my client needed several calendars provisioned via a Feature Receiver when a particular type of Site Collection was created; they had one primary calendar and they wanted all the other calendars to be overlaid onto the primary one using SharePoint 2010’s Calendar overlay capabilities.
Here’s a quick summary of this feature if you’re not familiar with it. When you are looking at a calendar you should notice that there is a ribbon button titled “Calendar Overlay”:

Clicking this button brings you to the Calendar Overlay Settings page; from this page you can add or edit the overlay calendars. Clicking “New Calendar” brings you to an application page where you can define what calendar to overlay:
As you can see from the above screenshot, you can add not just a SharePoint calendar as an overlay but also an Exchange calendar. After configuring calendar overlays you will see overlaid items when looking at the month view for the calendar:

In this example, the pink item is coming from the overlay calendar. Overlay items are added dynamically after the page loads (some JavaScript and Ajax take care of loading the items and rendering them on the page).
So that’s the basics from the end-users perspective. Now what about the technical details? Well, there’s not a lot of information out there that describes how this is done but here’s the gist of it – there’s a simple XML structure that is stored in the SPView’s CalendarSettings property; this property defines the overlays. Once you know that the rest is just a matter of figuring out what that structure looks like. The easiest way to start is to simply go through the browser and set up one or two overlays and then use some simple PowerShell to dump out that XML:
![SNAGHTML16554e82[4] SNAGHTML16554e82[4]](http://blog.falchionconsulting.com/wp-content/Images/Programmatically-Setting-SharePoint-2010_12AEE/SNAGHTML16554e824.png)
Here’s a better view of what that XML looks like:
<AggregationCalendars>
<AggregationCalendar Id="{26ddb82c-9e2b-4c5d-9b7e-4ee25cf5c357}"
Type="SharePoint"
Name="My Overlay Calendar"
Description=""
Color="5"
AlwaysShow="False"
CalendarUrl="/Lists/MyOverlayCalendar/calendar.aspx">
<Settings WebUrl="http://demo"
ListId="{428bd2cb-a32d-4867-b658-6498158636a8}"
ViewId="{09928cd4-9a5e-44ed-9bf2-dfe1fc85661b}"
ListFormUrl="/Lists/MyOverlayCalendar/DispForm.aspx" />
</AggregationCalendar>
</AggregationCalendars>
I want to call particular attention to the WebUrl attribute of the Settings element; this value *must* be the full URL of the SPWeb object that contains the overlay calendar list. Okay, you’re thinking, not a huge deal, SharePoint stores the full URL for lots of things and it doesn’t really pose issues right? WRONG! Think about a scenario where you have an extended web application. So in my example I have an authoring site located at http://demo and I’ve extended this site for anonymous access under the URL http://demo.aptillon.com. Due to what I consider a design flaw with the overlays, the overlay feature will only work when the web application you are accessing the site as matches the web application defined for the WebUrl attribute. So if I were to try and access my overlay using the anonymous site I’d get the following error:
And of course, due to this error, the overlays will not show up. So even if I’ve only changed the protocol (http to https) I’d still get this same error. This means that, effectively, calendar overlays using SharePoint lists will only work under the context of the Web Application (and protocol) from which the overlay was defined. (I’ve torn through the code that does this and it’s something that Microsoft should be very embarrassed about – very poor performance and just flat out horribly implemented. Okay, I digress, let’s get back to the details.
Another thing you’ll want to do to understand this XML structure is to look at the code that constructs it. There’s two places to look and both require Reflector or some equivalent disassembler; the first is the SerializeAccessors() method of the Microsoft.SharePoint.ApplicationPages.Calendar.CalendarAccessorManagerImpl class. This method takes the properties provided to it and constructs the XML structure shown above. So where are these properties set? For that we need to look at the BtnOk_Click() method of the Microsoft.SharePoint.ApplicationPages.AggregationCustomizePage class. I’m not going to show the details of these methods here but suffice it to say these methods have everything you need to understand this structure.
As I previously noted, for my particular client I needed to set several overlays within a Feature Activated event; to make this easier (because there was technically several places I had to do this) I created a simple method that I could call; this method took in my target list and the list I wanted to overlay as well as several other properties. For this post I’ve taken that code and created a modified version of it which supports adding Exchange-based calendars. Here’s that code:
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Xml; using Microsoft.SharePoint; namespace Lapointe.SharePoint2010.Automation.Common.Lists { public enum CalendarOverlayColor { LightYellow = 1, LightGreen = 2, Orange = 3, LightTurquise = 4, Pink = 5, LightBlue = 6, IceBlue1 = 7, IceBlue2 = 8, White = 9 } public class SetListOverlay { public static void AddCalendarOverlay(SPList targetList, string viewName, string owaUrl, string exchangeUrl, string overlayName, string overlayDescription, CalendarOverlayColor color, bool alwaysShow, bool clearExisting) { AddCalendarOverlay(targetList, viewName, owaUrl, exchangeUrl, null, overlayName, overlayDescription, color, alwaysShow, clearExisting); } public static void AddCalendarOverlay(SPList targetList, string viewName, SPList overlayList, string overlayName, string overlayDescription, CalendarOverlayColor color, bool alwaysShow, bool clearExisting) { AddCalendarOverlay(targetList, viewName, null, null, overlayList, overlayName, overlayDescription, color, alwaysShow, clearExisting); } private static void AddCalendarOverlay(SPList targetList, string viewName, string owaUrl, string exchangeUrl, SPList overlayList, string overlayName, string overlayDescription, CalendarOverlayColor color, bool alwaysShow, bool clearExisting) { bool sharePoint = overlayList != null; string linkUrl = owaUrl; if (sharePoint) linkUrl = overlayList.DefaultViewUrl; SPView targetView = targetList.DefaultView; if (!string.IsNullOrEmpty(viewName)) targetView = targetList.Views[viewName]; XmlDocument xml = new XmlDocument(); XmlElement aggregationElement = null; int count = 0; if (string.IsNullOrEmpty(targetView.CalendarSettings) || clearExisting) { xml.AppendChild(xml.CreateElement("AggregationCalendars")); aggregationElement = xml.CreateElement("AggregationCalendar"); xml.DocumentElement.AppendChild(aggregationElement); } else { xml.LoadXml(targetView.CalendarSettings); XmlNodeList calendars = xml.SelectNodes("/AggregationCalendars/AggregationCalendar"); if (calendars != null) count = calendars.Count; aggregationElement = xml.SelectSingleNode(string.Format("/AggregationCalendars/AggregationCalendar[@CalendarUrl='{0}']", linkUrl)) as XmlElement; if (aggregationElement == null) { if (count >= 10) throw new SPException(string.Format("10 calendar ovarlays already exist for the calendar {0}.",targetList.RootFolder.ServerRelativeUrl)); aggregationElement = xml.CreateElement("AggregationCalendar"); xml.DocumentElement.AppendChild(aggregationElement); } } if (!aggregationElement.HasAttribute("Id")) aggregationElement.SetAttribute("Id", Guid.NewGuid().ToString("B", CultureInfo.InvariantCulture)); aggregationElement.SetAttribute("Type", sharePoint ? "SharePoint" : "Exchange"); aggregationElement.SetAttribute("Name", !string.IsNullOrEmpty(overlayName) ? overlayName : (overlayList == null ? "" : overlayList.Title)); aggregationElement.SetAttribute("Description", !string.IsNullOrEmpty(overlayDescription) ? overlayDescription : (overlayList == null ? "" : overlayList.Description)); aggregationElement.SetAttribute("Color", ((int)color).ToString()); aggregationElement.SetAttribute("AlwaysShow", alwaysShow.ToString()); aggregationElement.SetAttribute("CalendarUrl", linkUrl); XmlElement settingsElement = aggregationElement.SelectSingleNode("./Settings") as XmlElement; if (settingsElement == null) { settingsElement = xml.CreateElement("Settings"); aggregationElement.AppendChild(settingsElement); } if (sharePoint) { settingsElement.SetAttribute("WebUrl", overlayList.ParentWeb.Site.MakeFullUrl(overlayList.ParentWebUrl)); settingsElement.SetAttribute("ListId", overlayList.ID.ToString("B", CultureInfo.InvariantCulture)); settingsElement.SetAttribute("ViewId", overlayList.DefaultView.ID.ToString("B", CultureInfo.InvariantCulture)); settingsElement.SetAttribute("ListFormUrl", overlayList.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl); } else { settingsElement.SetAttribute("ServiceUrl", exchangeUrl); } targetView.CalendarSettings = xml.OuterXml; targetView.Update(); /* <AggregationCalendars> <AggregationCalendar Id="{cfc22c0b-688e-4555-b1d0-784081a91464}" Type="SharePoint" Name="My Overlay Calendar" Description="" Color="1" AlwaysShow="True" CalendarUrl="/Lists/MyOverlayCalendar/calendar.aspx"> <Settings WebUrl="http://demo" ListId="{4a15e596-674f-4af7-a548-0b01470e8d75}" ViewId="{594c2916-14e7-4b08-ba36-1126b825bf45}" ListFormUrl="/Lists/MyOverlayCalendar/DispForm.aspx" /> </AggregationCalendar> <AggregationCalendar Id="{cfc22c0b-688e-4555-b1d0-784081a91465}" Type="Exchange" Name="My Overlay Calendar" Description="" Color="1" AlwaysShow="True" CalendarUrl="<url>"> <Settings ServiceUrl="<url>" /> </AggregationCalendar> </AggregationCalendars> */ } } }
I’m not going to bore you with the details of this code as all I’m doing is basic XML manipulation. I created a couple of method overloads to allow for creating SharePoint or Exchange-based overlays. So, did you notice the namespace? Yup, no point in releasing code here if I’m not going to turn it into a cmdlet ![]()
I’m not sure how useful this cmdlet will be in everyday use but imagine the scenario in which you have a primary calendar on your company portal and you want to add it as an overlay on every calendar throughout portal – you could easily do this using this cmdlet. Before we get to that, let’s see the full help for the cmdlet, which I called Set-SPListOverlay:
PS C:\Users\spadmin> help Set-SPListOverlay -full
NAME
Set-SPListOverlay
SYNOPSIS
Sets calendar overlays for the given list.
SYNTAX
Set-SPListOverlay -Color -OverlayTitle [-OverlayDescription ] -OwaUrl -WebServiceUrl [-TargetList] [-ViewName ] [-DoNotAlwaysShow ] [-ClearExisting ] [-AssignmentCollection ] []
Set-SPListOverlay -Color [-OverlayList] [-OverlayTitle ] [-OverlayDescription ] [-TargetList] [-ViewName ] [-DoNotAlwaysShow ] [-ClearExisting ] [-AssignmentCollection ] []
Set-SPListOverlay [-OverlayLists] [-TargetList] [-ViewName ] [-DoNotAlwaysShow ] [-ClearExisting ] [-AssignmentCollection ] []
DESCRIPTION
Sets calendar overlays for the given list.
Copyright 2010 Falchion Consulting, LLC
> For more information on this cmdlet and others:
> http://blog.falchionconsulting.com/
> Use of this cmdlet is at your own risk.
> Gary Lapointe assumes no liability.
PARAMETERS
-Color
The color to use for the overlay calendar.
Required? true
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
-TargetList
The calendar list to add the overlays to.
Required? true
Position? 1
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
-ViewName []
The name of the view to add the overlays to. If not specified the default view will be used.
Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
-OverlayList
The calendar list to add as an overlay.
Required? true
Position? 2
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
-OverlayLists
The calendar lists to add as an overlay.
Required? true
Position? 2
Default value
Accept pipeline input? false
Accept wildcard characters? false
-OverlayTitle []
The title to give the overlay calendar when viewed in the target calendar.
Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
-OverlayDescription []
The description to give the overlay calendar when viewed in the target calendar.
Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
-OwaUrl
Outlook Web Access URL.
Required? true
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
-WebServiceUrl
Exchange Web Service URL.
Required? true
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
-DoNotAlwaysShow []
Don't always show the calendar overlay.
Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
-ClearExisting []
Clear existing overlays. If not specified then all overlays will be appended to the list of existing over lays (up until 10 - anything after 10 will be ignored)
Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
-AssignmentCollection []
Manages objects for the purpose of proper disposal. Use of objects, such as SPWeb or SPSite, can use large amounts of memory and use of these objects in Windows PowerShell scripts requires proper memory management. Using the SPAssignment object, you can assign objects to a variable and dispose of the objects after they are needed to free up memory. When SPWeb, SPSite, or SPSiteAdministration objects are used, the objects are automatically disposed of if an assignment collection or the Global parameter is not used.
When the Global parameter is used, all objects are contained in the global store. If objects are not immediately used, or disposed of by using the Stop-SPAssignment command, an out-of-memory scenario can occur.
Required? false
Position? named
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
This cmdlet supports the common parameters: Verbose, Debug,
ErrorAction, ErrorVariable, WarningAction, WarningVariable,
OutBuffer and OutVariable. For more information, type,
"get-help about_commonparameters".
INPUTS
OUTPUTS
NOTES
For more information, type "Get-Help Set-SPListOverlay -detailed". For technical information, type "Get-Help Set-SPListOverlay -full".
------------------EXAMPLE------------------
PS C:\> Get-SPList "http://server_name/lists/MyCalendar" | Set-SPListOverlay -TargetList "http://server_name/lists/MyOverlayCalendar" -Color "Pink" -ClearExisting
This example adds the MyOverlayCalendar calendar as an overlay to the MyCalendar list.
RELATED LINKS
Get-SPList
You can see from the different parameter sets that I’ve made provisions for setting the overlay as a SharePoint list or an Exchange calendar; additionally, I’ve made it so that if you have an array of lists that you wish to add as an overlay you can easily pass that array in as well. So now let’s look at that example:
$mainList = Get-SPList http://demo/lists/myCalendar foreach ($site in (Get-SPSite http://demo -Limit All)) { foreach ($web in $site.AllWebs) { foreach ($list in ($web.Lists | ? {$_.BaseTemplate -eq "Events"})) { if ($list.ID -eq $mainList.ID) { continue } Set-SPListOverlay -TargetList $list ` -OverlayList $mainList ` -Color "Pink" ` -OverlayTitle "Main Portal Calendar" ` -ClearExisting } $web.Dispose() } $site.Dispose() }
Pretty simple huh? I’m just grabbing the primary list and then I’m looping through all my Site Collections and Sites and then grabbing all the lists that have a base template of “Events”. Once I have the list then I simply call my cmdlet – that’s it – easy right?
Okay, so what if you have a calendar with a bunch of overlays and you need to grab those calendars and do something to them? Well, I threw in a bonus cmdlet called Get-SPListOverlays. This one is really simple – it merely takes in the primary list and then calls a simple helper method that parses the XML structure and grabs each list. I’m not going to bother showing the code as it’s real basic and this post is long enough (just download the source if you’d like to see it) but I will show the cmdlet help:
PS C:\Users\spadmin> help Get-SPListOverlays -Full
NAME
Get-SPListOverlays
SYNOPSIS
Retrieve all SPList objects set as a calendar overlay on the given list.
SYNTAX
Get-SPListOverlays [-Identity] [-Web ] [-AssignmentCollection ] []
DESCRIPTION
Retrieve all SPList objects set as a calendar overlay on the given list.
Copyright 2010 Falchion Consulting, LLC
> For more information on this cmdlet and others:
> http://blog.falchionconsulting.com/
> Use of this cmdlet is at your own risk.
> Gary Lapointe assumes no liability.
PARAMETERS
-Identity
The calendar whose calendar overlays will be retrieved.
The value must be a valid URL in the form http://server_name/lists/listname or /lists/listname. If a server relative URL is provided then the Web parameter must be provided.
Required? true
Position? 1
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
-Web []
Specifies the URL or GUID of the Web containing the calendar whose overlays will be retrieved.
The type must be a valid GUID, in the form 12345678-90ab-cdef-1234-567890bcdefgh; a valid name of Microsoft SharePoint Foundation 2010 Web site (for example, MySPSite1); or an instance of a valid SPWeb object.
Required? false
Position? named
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
-AssignmentCollection []
Manages objects for the purpose of proper disposal. Use of objects, such as SPWeb or SPSite, can use large amounts of memory and use of these objects in Windows PowerShell scripts requires proper memory management. Using the SPAssignment object, you can assign objects to a variable and dispose of the objects after they are needed to free up memory. When SPWeb, SPSite, or SPSiteAdministration objects are used, the objects are automatically disposed of if an assignment collection or the Global parameter is not used.
When the Global parameter is used, all objects are contained in the global store. If objects are not immediately used, or disposed of by using the Stop-SPAssignment command, an out-of-memory scenario can occur.
Required? false
Position? named
Default value
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
This cmdlet supports the common parameters: Verbose, Debug,
ErrorAction, ErrorVariable, WarningAction, WarningVariable,
OutBuffer and OutVariable. For more information, type,
"get-help about_commonparameters".
INPUTS
OUTPUTS
NOTES
For more information, type "Get-Help Get-SPListOverlays -detailed". For technical information, type "Get-Help Get-SPListOverlays -full".
------------------EXAMPLE------------------
PS C:\> $lists = Get-SPListOverlays "http://server_name/lists/mylist"
This example retrieves the calendar overlays for the calendar at http://server_name/lists/mycalendar.
RELATED LINKS
Get-SPList
Set-SPListOverlay
Get-SPWeb
In the end this turned out to all be pretty simple to do but it was certainly a challenge trying to figure it all out as there’s no documentation (official or otherwise) that I’ve been able to find.
Setting List Properties using STSADM
Recently I had a reader of my blog send me a modified version of my gl-addlist command in which he added some additional properties to set a couple of the SPList properties (specifically the versioning settings). In thinking about this I decided that it might be helpful to have a command specifically for setting most of the SPList properties rather than try to incorporate them into a more general command like the gl-addlist command so I decided to create a new command that I called gl-setlistproperties.
Now ideally you would just use PowerShell to set list properties and you can do so pretty easily using my custom CmdLet: Get-SPList. Here’s an example of how you could do this using my CmdLet:
$list = Get-SPList http://portal/documents $list.EnableVersioning = $true $list.EnableMinorVersions = $true $list.EnableModeration = $true $list.Update()
For a full list of the properties that you can set see the SPList documentation in the SDK. But if you’re not a PowerShell guy and prefer batch files then you can use my gl-setlistproperties command instead. The code, as you can imagine, is really simple:
1: using System;
2: using System.Collections.Specialized;
3: using System.Text;
4: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
5: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
6: using Microsoft.SharePoint;
7:
8: namespace Lapointe.SharePoint.STSADM.Commands.Lists
9: {
10: public class SetListProperties : SPOperation
11: {
12: internal enum VersionSettings {None, Major, MajorMinor}
13: /// <summary>
14: /// Initializes a new instance of the <see cref="SetListProperties"/> class.
15: /// </summary>
16: public SetListProperties()
17: {
18: SPParamCollection parameters = new SPParamCollection();
19: parameters.Add(new SPParam("url", "url", true, null, new SPUrlValidator(), "Please specify URL to the list."));
20: SPEnumValidator versionValidator = new SPEnumValidator(typeof(VersionSettings));
21: parameters.Add(new SPParam("versioning", "version", false, null, versionValidator, "Please specify the version settings."));
22: SPEnumValidator draftVisibilityValidator = new SPEnumValidator(typeof(DraftVisibilityType));
23: parameters.Add(new SPParam("draftvisibility", "dvv", false, null, draftVisibilityValidator));
24: parameters.Add(new SPParam("majorversionlimit", "mvl", false, "0", new SPIntRangeValidator(0, 50000)));
25: parameters.Add(new SPParam("majorwithminorversionlimit", "mmvl", false, "0", new SPIntRangeValidator(0, 50000)));
26: parameters.Add(new SPParam("forcecheckout", "fc", false, null, new SPTrueFalseValidator()));
27: parameters.Add(new SPParam("enablemoderation", "mod", false, null, new SPTrueFalseValidator()));
28: parameters.Add(new SPParam("enablecontenttypes", "ect", false, null, new SPTrueFalseValidator()));
29: parameters.Add(new SPParam("enablefoldercreation", "efc", false, null, new SPTrueFalseValidator()));
30: parameters.Add(new SPParam("allowdeletion", "del", false, null, new SPTrueFalseValidator()));
31: parameters.Add(new SPParam("alloweveryoneviewitems", "aevi", false, null, new SPTrueFalseValidator()));
32: parameters.Add(new SPParam("enablesyndication", "syn", false, null, new SPTrueFalseValidator()));
33: parameters.Add(new SPParam("hidden", "hidden", false, null, new SPTrueFalseValidator()));
34: parameters.Add(new SPParam("onquicklaunch", "oql", false, null, new SPTrueFalseValidator()));
35: SPEnumValidator itemOpenValidator = new SPEnumValidator(typeof(DefaultItemOpen));
36: parameters.Add(new SPParam("defaultitemopen", "open", false, null, itemOpenValidator));
37: parameters.Add(new SPParam("ordered", "ordered", false, null, new SPTrueFalseValidator()));
38: parameters.Add(new SPParam("title", "title", false, null, new SPNullOrNonEmptyValidator()));
39: parameters.Add(new SPParam("description", "desc", false, null, new SPNullOrNonEmptyValidator()));
40: parameters.Add(new SPParam("nocrawl", "nocrawl", false, null, new SPTrueFalseValidator()));
41: parameters.Add(new SPParam("enableattachments", "att", false, null, new SPTrueFalseValidator()));
42:
43: StringBuilder sb = new StringBuilder();
44: sb.Append("\r\n\r\nSets various properties of a list.\r\n\r\nParameters:");
45: sb.Append("\r\n\t-url <URL to the list to update>");
46: sb.Append("\r\n\t[-title <title of the list>]");
47: sb.Append("\r\n\t[-description <title of the list>]");
48: sb.AppendFormat("\r\n\t[-versioning <{0}>", versionValidator.DisplayValue);
49: sb.AppendFormat("\r\n\t[-draftvisibility <{0}>", draftVisibilityValidator.DisplayValue);
50: sb.Append("\r\n\t[-majorversionlimit <0 - 50000>]");
51: sb.Append("\r\n\t[-majorwithminorversionlimit <0 - 50000>]");
52: sb.Append("\r\n\t[-forcecheckout <true | false>]");
53: sb.Append("\r\n\t[-enablemoderation <true | false>]");
54: sb.Append("\r\n\t[-enablecontenttypes <true | false>]");
55: sb.Append("\r\n\t[-enablefoldercreation <true | false>]");
56: sb.Append("\r\n\t[-allowdeletion <true | false>]");
57: sb.Append("\r\n\t[-alloweveryoneviewitems <true | false>]");
58: sb.Append("\r\n\t[-enablesyndication <true | false>]");
59: sb.Append("\r\n\t[-hidden <true | false>]");
60: sb.Append("\r\n\t[-onquicklaunch <true | false>]");
61: sb.AppendFormat("\r\n\t[-defaultitemopen <{0}>", itemOpenValidator.DisplayValue);
62: sb.Append("\r\n\t[-ordered <true | false>]");
63: sb.Append("\r\n\t[-nocrawl <true | false>]");
64: sb.Append("\r\n\t[-enableattachments <true | false>]");
65: Init(parameters, sb.ToString());
66: }
67:
68:
69: #region ISPStsadmCommand Members
70:
71: /// <summary>
72: /// Gets the help message.
73: /// </summary>
74: /// <param name="command">The command.</param>
75: /// <returns></returns>
76: public override string GetHelpMessage(string command)
77: {
78: return HelpMessage;
79: }
80:
81: /// <summary>
82: /// Runs the specified command.
83: /// </summary>
84: /// <param name="command">The command.</param>
85: /// <param name="keyValues">The key values.</param>
86: /// <param name="output">The output.</param>
87: /// <returns></returns>
88: public override int Execute(string command, StringDictionary keyValues, out string output)
89: {
90: output = string.Empty;
91: Verbose = true;
92:
93: string url = Params["url"].Value.TrimEnd('/');
94:
95: using (SPSite site = new SPSite(url))
96: using (SPWeb web = site.OpenWeb())
97: {
98: SPList list = Utilities.GetListFromViewUrl(web, url);
99:
100: if (list == null)
101: throw new SPException("List was not found.");
102:
103:
104: if (Params["versioning"].UserTypedIn)
105: {
106: VersionSettings versioning =
107: (VersionSettings) Enum.Parse(typeof (VersionSettings), Params["versioning"].Value, true);
108: SetVersioning(list, versioning);
109: }
110:
111:
112: SetProperties(list,
113: GetValue(list.Title, "title"),
114: GetValue(list.Title, "description"),
115: GetValue(list.ContentTypesEnabled, "enablecontenttypes"),
116: GetValue(list.DraftVersionVisibility, "draftvisibility"),
117: GetValue(list.MajorVersionLimit, "majorversionlimit"),
118: GetValue(list.MajorWithMinorVersionsLimit, "majorwithminorversionlimit"),
119: GetValue(list.ForceCheckout, "forcecheckout"),
120: GetValue(list.EnableModeration, "enablemoderation"),
121: GetValue(list.EnableFolderCreation, "enablefoldercreation"),
122: GetValue(list.AllowDeletion, "allowdeletion"),
123: GetValue(list.AllowEveryoneViewItems, "alloweveryoneviewitems"),
124: GetValue(list.EnableSyndication, "enablesyndication"),
125: GetValue(list.DefaultItemOpen, "defaultitemopen"),
126: GetValue(list.Hidden, "hidden"),
127: GetValue(list.OnQuickLaunch, "onquicklaunch"),
128: GetValue(list.Ordered, "ordered"),
129: GetValue(list.NoCrawl, "nocrawl"),
130: GetValue(list.EnableAttachments, "enableattachments"));
131:
132: list.Update();
133: }
134:
135: return OUTPUT_SUCCESS;
136: }
137:
138: #endregion
139:
140: /// <summary>
141: /// Gets the value.
142: /// </summary>
143: /// <typeparam name="T"></typeparam>
144: /// <param name="defaultValue">The default value.</param>
145: /// <param name="paramName">Name of the param.</param>
146: /// <returns></returns>
147: private T GetValue<T>(T defaultValue, string paramName)
148: {
149: if (!Params[paramName].UserTypedIn)
150: return defaultValue;
151:
152: string val = Params[paramName].Value;
153: if (typeof(T).IsEnum)
154: return (T) Enum.Parse(typeof (T), val, true);
155:
156: return (T)Convert.ChangeType(val, typeof (T));
157:
158: }
159:
160: /// <summary>
161: /// Sets the properties.
162: /// </summary>
163: /// <param name="list">The list.</param>
164: /// <param name="title">The title.</param>
165: /// <param name="description">The description.</param>
166: /// <param name="contentTypesEnabled">if set to <c>true</c> [content types enabled].</param>
167: /// <param name="draftVisibility">The draft visibility.</param>
168: /// <param name="majorVersionLimit">The major version limit.</param>
169: /// <param name="majorWithMinorVersionLimit">The major with minor version limit.</param>
170: /// <param name="forceCheckout">if set to <c>true</c> [force checkout].</param>
171: /// <param name="enableModeration">if set to <c>true</c> [enable moderation].</param>
172: /// <param name="enableFolderCreation">if set to <c>true</c> [enable folder creation].</param>
173: /// <param name="allowDeletion">if set to <c>true</c> [allow deletion].</param>
174: /// <param name="allowEveryoneViewItems">if set to <c>true</c> [allow everyone view items].</param>
175: /// <param name="enableSyndication">if set to <c>true</c> [enable syndication].</param>
176: /// <param name="itemOpen">The item open.</param>
177: /// <param name="hidden">if set to <c>true</c> [hidden].</param>
178: /// <param name="onQuickLaunch">if set to <c>true</c> [on quick launch].</param>
179: /// <param name="ordered">if set to <c>true</c> [ordered].</param>
180: /// <param name="noCrawl">if set to <c>true</c> [no crawl].</param>
181: /// <param name="enableAttachments">if set to <c>true</c> [enable attachments].</param>
182: private void SetProperties(SPList list,
183: string title,
184: string description,
185: bool contentTypesEnabled,
186: DraftVisibilityType draftVisibility,
187: int majorVersionLimit,
188: int majorWithMinorVersionLimit,
189: bool forceCheckout,
190: bool enableModeration,
191: bool enableFolderCreation,
192: bool allowDeletion,
193: bool allowEveryoneViewItems,
194: bool enableSyndication,
195: DefaultItemOpen itemOpen,
196: bool hidden,
197: bool onQuickLaunch,
198: bool ordered,
199: bool noCrawl,
200: bool enableAttachments)
201: {
202: list.Title = title;
203: list.Description = description;
204:
205: if (list.AllowContentTypes || !contentTypesEnabled)
206: list.ContentTypesEnabled = contentTypesEnabled;
207:
208: list.DraftVersionVisibility = draftVisibility;
209:
210: if (enableModeration && list.BaseTemplate == SPListTemplateType.PictureLibrary)
211: Log("WARNING: Cannot set moderation on a picture library.");
212: else
213: list.EnableModeration = enableModeration;
214:
215: if (list.EnableVersioning)
216: list.MajorVersionLimit = majorVersionLimit;
217:
218: if (list.EnableMinorVersions && list.EnableModeration)
219: list.MajorWithMinorVersionsLimit = majorWithMinorVersionLimit;
220:
221:
222: if (list.BaseTemplate == SPListTemplateType.DocumentLibrary)
223: list.ForceCheckout = forceCheckout;
224: else if (forceCheckout)
225: Log("WARNING: Force checkout can only be set on document libraries.");
226:
227: list.EnableFolderCreation = enableFolderCreation;
228: list.AllowDeletion = allowDeletion;
229: list.AllowEveryoneViewItems = allowEveryoneViewItems;
230: list.EnableSyndication = enableSyndication;
231: list.DefaultItemOpen = itemOpen;
232: list.Hidden = hidden;
233: list.OnQuickLaunch = onQuickLaunch;
234: list.NoCrawl = noCrawl;
235:
236: if (list.BaseTemplate == SPListTemplateType.GenericList)
237: list.Ordered = ordered;
238: else if (ordered)
239: Log("WARNING: The Ordered property can only be set for generic lists.");
240:
241: if (!enableAttachments)
242: list.EnableAttachments = false;
243: else
244: {
245: if (!(list.BaseType == SPBaseType.DocumentLibrary || list.BaseType == SPBaseType.Survey))
246: list.EnableVersioning = true;
247: else
248: Log("WARNING: Attachments are only allowed on document libraries and surveys.");
249: }
250:
251:
252: }
253:
254: /// <summary>
255: /// Sets the versioning.
256: /// </summary>
257: /// <param name="list">The list.</param>
258: /// <param name="versioning">The versioning.</param>
259: private void SetVersioning(SPList list, VersionSettings versioning)
260: {
261: switch (versioning)
262: {
263: case VersionSettings.None:
264: list.EnableVersioning = false;
265: list.EnableMinorVersions = false;
266: break;
267: case VersionSettings.Major:
268: list.EnableVersioning = true;
269: list.EnableMinorVersions = false;
270: break;
271: case VersionSettings.MajorMinor:
272: list.EnableVersioning = true;
273: list.EnableMinorVersions = true;
274: break;
275: }
276: }
277: }
278: }
The help for the command is shown below:
C:\>stsadm -help gl-setlistproperties stsadm -o gl-setlistproperties Sets various properties of a list. Parameters: -url <URL to the list to update> [-title <title of the list>] [-description <title of the list>] [-versioning <none | major | majorminor> [-draftvisibility <reader | author | approver> [-majorversionlimit <0 - 50000>] [-majorwithminorversionlimit <0 - 50000>] [-forcecheckout <true | false>] [-enablemoderation <true | false>] [-enablecontenttypes <true | false>] [-enablefoldercreation <true | false>] [-allowdeletion <true | false>] [-alloweveryoneviewitems <true | false>] [-enablesyndication <true | false>] [-hidden <true | false>] [-onquicklaunch <true | false>] [-defaultitemopen <preferclient | browser> [-ordered <true | false>] [-nocrawl <true | false>] [-enableattachments <true | false>] |
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-setlistproperties | WSS 3.0
MOSS 2007 |
Released: 7/6/2009
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL of the list or library to update. | -url http://portal/documents | |
| title | No | String representing the title of the list. Corresponds to the SPList.Title property. | -title Documents | |
| description | desc | No | String representing the description of the list. Corresponds to the SPList.Description property. | -description "Shared Documents"
-desc "Shared Documents" |
| versioning | version | No | Sets whether to enable versioning and if minor versions should be created or not (sets the SPList.EnableVersioning and the SPList.EnableMinorVersions properties). Valid values are “none”, “major”, and “majorminor”. | -versioning majorminor
-version majorminor |
| draftvisibility | dvv | No | Sets a value that determines the type of user who can view minor versions of document drafts within the list. Corresponds to the SPList.DraftVersionVisibility property. Valid values are “reader”, “author”, and “approver”. | -draftvisibility approver
-dvv approver |
| majorversionlimit | mvl | No |
Sets the maximum number of major versions allowed for an item in a document library that uses version control with major versions only. Corresponds to the SPList.MajorVersionLimit property. |
-majorversionlimit 3
-mvl 3 |
| majorwithminorversionlimit | mmvl | No | Sets the maximum number of major versions that are allowed for an item in a document library that uses version control with both major and minor versions. Corresponds to the SPList.MajorWithMinorVersionsLimit property. | -majorwithminorversionlimit 3
-mmvl 3 |
| forcecheckout | fc | No | Sets whether forced checkout is enabled for the document library. Valid values are “true” and “false”. Corresponds to the SPList.ForceCheckout property. | -forcecheckout true
-fc true |
| enablemoderation | mod | No | Sets whether Content Approval is enabled for the list. Valid values are “true” and “false”. Corresponds to the SPList.EnableModeration property. | -enablemoderation true
-mod true |
| enablecontenttypes | ect | No | Sets whether content types are enabled for the list. Valid values are “true” and “false”. Corresponds to the SPList.ContentTypesEnabled property. | -enablecontenttypes true
-ect true |
| enablefoldercreation | efc | No | Sets whether folders can be created for the list. Valid values are “true” and “false”. Corresponds to the SPList.EnableFolderCreation property. | -enablefoldercreation true
-efc true |
| allowdeletion | del | No | Sets whether the list can be deleted. Valid values are “true” and “false”. Corresponds to the SPList.AllowDeletion property. | -allowdeletion true
-del true |
| alloweveryoneviewitems | aevi | No | Sets whether everyone can view documents in the document library or attachments to items in the list. Valid values are “true” and “false”. Corresponds to the SPList.AllowEveryoneViewItems property. | -alloweveryoneviewitems true
-aevi true |
| enablesyndication | syn | No | Sets whether RSS syndication is enabled for the list. Valid values are “true” and “false”. Corresponds to the SPList.EnableSyndication property. | -enablesyndication true
-syn true |
| hidden | No | Sets whether the list is hidden. Valid values are “true” and “false”. Corresponds to the SPList.Hidden property. | -hidden false | |
| onquicklaunch | oql | No | Sets whether the list appears on the Quick Launch of the home page. Valid values are “true” and “false”. Corresponds to the SPList.OnQuickLaunch property. | -onquicklaunch true
-oql true |
| defaultitemopen | open | No | Sets whether to open list items in a client application or in the browser. Valid values are “preferclient” and “browser”. Corresponds to the SPList.DefaultItemOpen property. | -defaultitemopen preferclient
-open preferclient |
| ordered | No | Sets whether the option to allow users to reorder items in the list is available on the Edit View page for the list. Valid values are “true” and “false”. Corresponds to the SPList.Ordered property. | -ordered true | |
| nocrawl | No | Sets whether crawling is enabled for the list. Valid values are “true” and “false”. Corresponds to the SPList.NoCrawl property. | -nocrawl false | |
| enableattachments | att | No | Sets whether attachments can be added to items in the list. Valid values are “true” and “false”. Corresponds to the SPList.EnableAttachments property. | -enableattachments false
-att false |
The following is an example of how to set a few properties on a list:
stsadm -o gl-setlistproperties -url http://portal/documents -versioning majorminor -enablemoderation true -draftvisibility approver
Note that I'm not exposing every property on the SPList class but I believe I've hit the more widely used ones.
Listing Event Receivers using STSADM
This post wraps up my event receiver posts. I just finished documenting the gl-addeventreceiver and gl-deleteeventreceiver commands and this final command would be gl-enumeventreceivers.
This command is the simplest of the three as it's just looping through all the event receivers belonging to the specified target and dumping the results out as XML. I've tried to structure the XML so that it conforms to the CAML schema therefore allowing the results to be put into a Feature if desired.
1: /// <summary>
2: /// Gets the XML.
3: /// </summary>
4: /// <param name="url">The URL.</param>
5: /// <param name="contentTypeName">Name of the content type.</param>
6: /// <param name="target">The target.</param>
7: /// <returns></returns>
8: public static string GetXml(string url, string contentTypeName, TargetEnum target)
9: {
10: using (SPSite site = new SPSite(url))
11: using (SPWeb web = site.OpenWeb())
12: {
13: SPEventReceiverDefinitionCollection eventReceivers;
14: if (target == TargetEnum.List)
15: {
16: SPList list = Utilities.GetListFromViewUrl(web, url);
17:
18: if (list == null)
19: {
20: throw new Exception("List not found.");
21: }
22: eventReceivers = list.EventReceivers;
23: }
24: else if (target == TargetEnum.Site)
25: eventReceivers = web.EventReceivers;
26: else
27: {
28: SPContentType contentType = null;
29: try
30: {
31: contentType = web.AvailableContentTypes[contentTypeName];
32: }
33: catch (ArgumentException)
34: {
35: }
36: if (contentType == null)
37: throw new SPSyntaxException("The specified content type could not be found.");
38:
39: eventReceivers = contentType.EventReceivers;
40: }
41: StringBuilder sb = new StringBuilder();
42: XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
43: xmlWriter.Formatting = Formatting.Indented;
44:
45: xmlWriter.WriteStartElement("Receivers");
46: foreach (SPEventReceiverDefinition erd in eventReceivers)
47: {
48: xmlWriter.WriteStartElement("Receiver");
49:
50: xmlWriter.WriteElementString("Name", erd.Name);
51: xmlWriter.WriteElementString("Type", erd.Type.ToString());
52: xmlWriter.WriteElementString("Assembly", erd.Assembly);
53: xmlWriter.WriteElementString("Class", erd.Class);
54: xmlWriter.WriteElementString("SequenceNumber", erd.SequenceNumber.ToString());
55:
56: xmlWriter.WriteEndElement();
57: }
58: xmlWriter.WriteEndElement();
59:
60: return sb.ToString();
61: }
62: }
The help for the command is shown below:
C:\>stsadm -help gl-enumeventreceivers
stsadm -o gl-enumeventreceivers
Enumerates all event receivers associated with the specified target object and outputs the list of receivers as XML.
Parameters:
-url <web or list URL>
-target <site | list | contenttype>
[-contenttype <content type name if target is ContentType>]
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-enumeventreceivers | WSS v3, MOSS 2007 | Released: 9/13/2008
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL to the web or list to display the event receivers. | -url http://portal/pages | |
| target | No | The target object to display the event receivers from. Must be either "list", "site", or "contenttype". If omitted defaults to "list". | -target list | |
| contenttype | ct | No, unless target is contenttype | The name of the content type to to get the event receivers from. | -contenttype "Page"
-ct "Page" |
The following is an example of how to display all the event receivers associated with a pages library:
stsadm -o gl-enumeventreceivers -url http://portal/pages -target list
The following is an example output from running the above command:
<Receivers><Receiver><Name /><Type>ItemUpdating</Type><Assembly>Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08</Assembly><Class>Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver</Class><SequenceNumber>10000</SequenceNumber></Receiver><Receiver><Name>PagesListEventReceiverName</Name><Type>ItemDeleting</Type><Assembly>Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly><Class>Microsoft.SharePoint.Publishing.PagesListCPVEventReceiver</Class><SequenceNumber>1005</SequenceNumber></Receiver><Receiver><Name>PagesListEventReceiverName</Name><Type>ItemAdded</Type><Assembly>Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly><Class>Microsoft.SharePoint.Publishing.PagesListCPVEventReceiver</Class><SequenceNumber>1004</SequenceNumber></Receiver><Receiver><Name>PagesListEventReceiverName</Name><Type>ItemUpdated</Type><Assembly>Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly><Class>Microsoft.SharePoint.Publishing.PagesListCPVEventReceiver</Class><SequenceNumber>1002</SequenceNumber></Receiver><Receiver><Name /><Type>ItemUpdated</Type><Assembly>Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08</Assembly><Class>Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver</Class><SequenceNumber>10000</SequenceNumber></Receiver><Receiver><Name>PagesListEventReceiverName</Name><Type>ItemDeleted</Type><Assembly>Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly><Class>Microsoft.SharePoint.Publishing.PagesListCPVEventReceiver</Class><SequenceNumber>1006</SequenceNumber></Receiver><Receiver><Name>PagesListEventReceiverName</Name><Type>ItemCheckedIn</Type><Assembly>Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly><Class>Microsoft.SharePoint.Publishing.PagesListCPVEventReceiver</Class><SequenceNumber>1003</SequenceNumber></Receiver><Receiver><Name /><Type>ItemCheckedIn</Type><Assembly>Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08</Assembly><Class>Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver</Class><SequenceNumber>10000</SequenceNumber></Receiver></Receivers>
Deleting an Event Receiver using STSADM
I had some time so I decided to add the counterparts to the gl-addeventreceiver command that I just published. I created two new commands, gl-deleteeventreceiver and gl-enumeventreceivers. This post will cover the delete command and I'll go over the enum command in my next post.
From a code perspective the delete command is very similar to the add command. The main difference (aside from the obvious call to Delete instead of Add) is that I've made the assembly, class, and type parameters optional (though at least one must be provided) so the code to get the event receiver to delete needed to be expanded so that it now evaluates each receiver and checks to see if a valid match is found. Once all the receivers have been identified then we simply delete them all.
1: /// <summary>
2: /// Deletes an event receiver from the specified target
3: /// </summary>
4: /// <param name="url">The URL.</param>
5: /// <param name="contentTypeName">Name of the content type.</param>
6: /// <param name="target">The target.</param>
7: /// <param name="assembly">The assembly.</param>
8: /// <param name="className">Name of the class.</param>
9: /// <param name="type">The type.</param>
10: /// <param name="typeNotProvided">if set to <c>true</c> [type not provided].</param>
11: public static void Delete(string url, string contentTypeName, TargetEnum target, string assembly, string className, SPEventReceiverType type, bool typeNotProvided)
12: {
13: using (SPSite site = new SPSite(url))
14: using (SPWeb web = site.OpenWeb())
15: {
16: SPEventReceiverDefinitionCollection eventReceivers;
17: if (target == TargetEnum.List)
18: {
19: SPList list = Utilities.GetListFromViewUrl(web, url);
20:
21: if (list == null)
22: {
23: throw new Exception("List not found.");
24: }
25: eventReceivers = list.EventReceivers;
26: }
27: else if (target == TargetEnum.Site)
28: eventReceivers = web.EventReceivers;
29: else
30: {
31: SPContentType contentType = null;
32: try
33: {
34: contentType = web.AvailableContentTypes[contentTypeName];
35: }
36: catch (ArgumentException)
37: {
38: }
39: if (contentType == null)
40: throw new SPSyntaxException("The specified content type could not be found.");
41:
42: eventReceivers = contentType.EventReceivers;
43: }
44: Delete(eventReceivers, type, assembly, className, typeNotProvided);
45: }
46: }
47:
48: /// <summary>
49: /// Deletes the event receiver matching the provided values from the passed in collection.
50: /// </summary>
51: /// <param name="eventReceivers">The event receivers.</param>
52: /// <param name="eventReceiverType">Type of the event receiver.</param>
53: /// <param name="assembly">The assembly.</param>
54: /// <param name="className">Name of the class.</param>
55: /// <param name="typeNotProvided">if set to <c>true</c> [type not provided].</param>
56: private static void Delete(SPEventReceiverDefinitionCollection eventReceivers, SPEventReceiverType eventReceiverType, string assembly, string className, bool typeNotProvided)
57: {
58: List<SPEventReceiverDefinition> toDelete = new List<SPEventReceiverDefinition>();
59: foreach (SPEventReceiverDefinition erd in eventReceivers)
60: {
61: if ((erd.Assembly == assembly || assembly == null) &&
62: (erd.Class == className || className == null) &&
63: ((erd.Type == eventReceiverType && !typeNotProvided) || typeNotProvided))
64: {
65: toDelete.Add(erd);
66: }
67: }
68: foreach (SPEventReceiverDefinition erd in toDelete)
69: {
70: Log("Deleting the {0} event for class {1} in assembly {2}.", erd.Type.ToString(), erd.Class,
71: erd.Assembly);
72:
73: erd.Delete();
74: }
75: }
The help for the command is shown below:
C:\>stsadm -help gl-deleteeventreceiver
stsadm -o gl-deleteeventreceiver
Deletes an event receiver from a list, web, or content type.
Parameters:
-url <web or list URL>
-target <site | list | contenttype>
[-assembly <assembly>]
[-class <class name>]
[-type <itemadding | itemupdating | itemdeleting | itemcheckingin | itemcheckingout | itemuncheckingout | itemattachmentadding | itemattachmentdeleting | itemfilemoving | fieldadding | fieldupdating | fielddeleting | sitedeleting | webdeleting | webmoving | itemadded | itemupdated | itemdeleted | itemcheckedin | itemcheckedout | itemuncheckedout | itemattachmentadded | itemattachmentdeleted | itemfilemoved | itemfileconverted | fieldadded | fieldupdated | fielddeleted | sitedeleted | webdeleted | webmoved | emailreceived | contextevent | invalidreceiver>]
[-contenttype <content type name if target is ContentType>]
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-deleteeventreceiver | WSS v3, MOSS 2007 | Released: 9/13/2008
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL to the web or list to remove the event receiver from. | -url http://portal/pages | |
| assembly | a | No | The fully qualified assembly name containing the event receiver class to delete from the target. | -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08"
-a "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" |
| class | c | No | The fully qualified class name of the event receiver to delete from the target. | -class Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver
-c Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver |
| type | No | The event type to delete. | -type itemupdated | |
| target | No | The target type of which the receiver will be deleted. Must be either "list", "site", or "contenttype". If omitted defaults to "list". | -target list | |
| contenttype | ct | No, unless target is contenttype | The name of the content type to remove the event receiver from if the target is contenttype. | -contenttype "Page"
-ct "Page" |
The following is an example of how to delete all the event receivers belonging to the web part page history assembly:
stsadm -o gl-deleteeventreceiver -url http://portal/pages -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" -target list
Note that you should be particularly careful when deleting event receivers and not specifying the assembly and class attributes as you could inadvertently delete event receivers that are critical to the normal functioning of the list.
Adding Event Receivers using STSADM
A few months ago I created a CodePlex project that allows you to configure a library to allow the storing of web part page history. The project at its core used three item event receivers to listen to certain key events and either backup the page or restore the page based on the event. Recently I was thinking about what might be the best way to allow people to script the settings that enable the storing of page history. I decided that I would start off by providing a simple STSADM command that would allow you to script the adding of event receivers to a list (fortunately making it work for webs and content types didn't really require any additional work so I covered those as well). The new command I created is called gl-addeventreceiver.
I'll probably eventually come up with something else but this at least is a start - I primarily don't like it because it requires that people know how the web part history project works so I'll probably create a command specific to enabling the history so that it abstracts out the technical details which could always change (there just hasn't been a whole lot of interest in the project so I've not really spent much time on it). In the meantime this command is a nice generic tool that could be used by developers and administrators for various reasons (I'll probably create an enum and remove command to supplement this one - just haven't gotten around to it yet).
Adding an event receiver via code is really very simple - just a matter of calling the Add method of the SPEventReceiverDefinitionCollection object which you can get via the EventReceivers property of either the SPList, SPContentType, or SPWeb object. To get an existing event receiver (to check for existence as we don't want to add twice) we have to loop through the collection comparing the assembly name, class name, and event receiver type.
1: /// <summary>
2: /// Adds an event receiver to the specified target
3: /// </summary>
4: /// <param name="url">The URL.</param>
5: /// <param name="contentTypeName">Name of the content type.</param>
6: /// <param name="target">The target.</param>
7: /// <param name="assembly">The assembly.</param>
8: /// <param name="className">Name of the class.</param>
9: /// <param name="type">The type.</param>
10: /// <param name="sequence">The sequence.</param>
11: /// <param name="name">The name.</param>
12: public static void Add(string url, string contentTypeName, TargetEnum target, string assembly, string className, SPEventReceiverType type, int sequence, string name)
13: {
14: using (SPSite site = new SPSite(url))
15: using (SPWeb web = site.OpenWeb())
16: {
17: SPEventReceiverDefinitionCollection eventReceivers;
18: if (target == TargetEnum.List)
19: {
20: SPList list = Utilities.GetListFromViewUrl(web, url);
21:
22: if (list == null)
23: {
24: throw new Exception("List not found.");
25: }
26: eventReceivers = list.EventReceivers;
27: }
28: else if (target == TargetEnum.Site)
29: eventReceivers = web.EventReceivers;
30: else
31: {
32: SPContentType contentType = null;
33: try
34: {
35: contentType = web.AvailableContentTypes[contentTypeName];
36: }
37: catch (ArgumentException)
38: {
39: }
40: if (contentType == null)
41: throw new SPSyntaxException("The specified content type could not be found.");
42:
43: eventReceivers = contentType.EventReceivers;
44: }
45: SPEventReceiverDefinition def = Add(eventReceivers, type, assembly, className, name);
46: if (sequence >= 0)
47: {
48: def.SequenceNumber = sequence;
49: def.Update();
50: }
51: }
52: }
53:
54: /// <summary>
55: /// Adds an event receiver to a the specified event receiver definition collection.
56: /// </summary>
57: /// <param name="eventReceivers">The event receivers.</param>
58: /// <param name="eventReceiverType">Type of the event receiver.</param>
59: /// <param name="assembly">The assembly.</param>
60: /// <param name="className">Name of the class.</param>
61: /// <param name="name">The name.</param>
62: /// <returns></returns>
63: private static SPEventReceiverDefinition Add(SPEventReceiverDefinitionCollection eventReceivers, SPEventReceiverType eventReceiverType, string assembly, string className, string name)
64: {
65: if (GetEventReceiver(eventReceivers, eventReceiverType, assembly, className) == null)
66: {
67: eventReceivers.Add(eventReceiverType, assembly, className);
68: SPEventReceiverDefinition def = GetEventReceiver(eventReceivers, eventReceiverType, assembly, className);
69: if (!string.IsNullOrEmpty(name))
70: {
71: def.Name = name;
72: def.Update();
73: }
74: return def;
75: }
76: return null;
77: }
78:
79: /// <summary>
80: /// Gets the event receiver.
81: /// </summary>
82: /// <param name="eventReceivers">The event receivers.</param>
83: /// <param name="eventReceiverType">Type of the event receiver.</param>
84: /// <param name="assembly">The assembly.</param>
85: /// <param name="className">Name of the class.</param>
86: /// <returns></returns>
87: private static SPEventReceiverDefinition GetEventReceiver(SPEventReceiverDefinitionCollection eventReceivers, SPEventReceiverType eventReceiverType, string assembly, string className)
88: {
89: foreach (SPEventReceiverDefinition erd in eventReceivers)
90: {
91: if (erd.Assembly == assembly && erd.Class == className && erd.Type == eventReceiverType)
92: {
93: return erd;
94: }
95: }
96: return null;
97: }
The help for the command is shown below:
C:\>stsadm -help gl-addeventreceiver
stsadm -o gl-addeventreceiver
Adds an event receiver to a list, web, or content type.
Parameters:
-url <web or list URL>
-assembly <assembly>
-class <class name>
-type <itemadding | itemupdating | itemdeleting | itemcheckingin | itemcheckingout | itemuncheckingout | itemattachmentadding | itemattachmentdeleting | itemfilemoving | fieldadding | fieldupdating | fielddeleting | sitedeleting | webdeleting | webmoving | itemadded | itemupdated | itemdeleted | itemcheckedin | itemcheckedout | itemuncheckedout | itemattachmentadded | itemattachmentdeleted | itemfilemoved | itemfileconverted | fieldadded | fieldupdated | fielddeleted | sitedeleted | webdeleted | webmoved | emailreceived | contextevent | invalidreceiver>
-target <site | list | contenttype>
[-contenttype <content type name if target is ContentType>]
[-sequence <sequence number>]
[-name <the name to give to the event receiver>]
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-addeventreceiver | WSS v3, MOSS 2007 | Released: 9/13/2008
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL to the web or list to add the event receiver to. | -url http://portal/pages | |
| assembly | a | Yes | The fully qualified assembly name containing the event receiver class to add. | -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08"
-a "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" |
| class | c | Yes | The fully qualified class name of the event receiver to add. | -class Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver
-c Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver |
| type | Yes | The event type to add. The command does not validate that you are adding the correct type for the specified target or that the specified class contains handlers for the type specified. | -type itemupdated | |
| target | No | The target type to add the event receiver to. Must be either "list", "site", or "contenttype". If omitted defaults to "list". | -target list | |
| contenttype | ct | No, unless target is contenttype | The name of the content type to add the event receiver to if the target is contenttype. | -contenttype "Page"
-ct "Page" |
| sequence | seq | No | The sequence number specifies the order of execution of the event receiver. | -sequence 1000
-seq 1000 |
| name | n | No | The name to give to the event receiver. The name has no significance but can be useful when later listing the event receivers. | -name "Handle Saving of Page History"
-n "Handle Saving of Page History" |
The following is an example of how to add three event receivers to a pages library - the three commands illustrated below constitute the required event receivers that must be enabled to turn on the web part page history:
stsadm -o gl-addeventreceiver -url http://portal/pages -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" -class "Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver" -target list -sequence 1000 -type itemcheckedin
stsadm -o gl-addeventreceiver -url http://portal/pages -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" -class "Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver" -target list -sequence 1000 -type itemupdating
stsadm -o gl-addeventreceiver -url http://portal/pages -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" -class "Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver" -target list -sequence 1000 -type itemupdated
Create a Publishing Page via STSADM
I'm working on a project which is going to require the creation of about a hundred publishing pages which were going to have to be created by hand. I really didn't want to have to sit there going through the UI to create all those pages so I threw together a new STSADM command that I could use via a script to create the pages: gl-createpublishingpage.
Creating publishing pages via code is pretty simple - you just use the PublishingWeb object's GetPublishingPages() method to return back the collection of pages and then call its Add method passing in the page name and layout. From there it's just a matter of setting the field properties.
1: public static void CreatePage(string url, string pageName, string title, string layoutName, Dictionary<string, string> fieldDataCollection)
2: {
3: using (SPSite site = new SPSite(url))
4: using (SPWeb web = site.AllWebs[Utilities.GetServerRelUrlFromFullUrl(url)])
5: {
6: if (!PublishingWeb.IsPublishingWeb(web))
7: throw new ArgumentException("The specified web is not a publishing web.");
8:
9: PublishingWeb pubweb = PublishingWeb.GetPublishingWeb(web);
10: PageLayout layout = null;
11: string availableLayouts = string.Empty;
12: foreach (PageLayout lo in pubweb.GetAvailablePageLayouts())
13: {
14: availableLayouts += "\t" + lo.Name + "\r\n";
15: if (lo.Name.ToLowerInvariant() == layoutName.ToLowerInvariant())
16: {
17: layout = lo;
18: break;
19: }
20: }
21: if (layout == null)
22: throw new ArgumentException("The layout specified could not be found. Available layouts are:\r\n" + availableLayouts);
23:
24: if (!pageName.ToLowerInvariant().EndsWith(".aspx"))
25: pageName += ".aspx";
26:
27: PublishingPage page = pubweb.GetPublishingPages().Add(pageName, layout);
28: page.Title = title;
29: SPListItem item = page.ListItem;
30:
31: foreach (string fieldName in fieldDataCollection.Keys)
32: {
33: string fieldData = fieldDataCollection[fieldName];
34:
35: try
36: {
37: SPField field = item.Fields.GetFieldByInternalName(fieldName);
38:
39: if (field.ReadOnlyField)
40: {
41: Console.WriteLine("Field '{0}' is read only and will not be updated.", field.InternalName);
42: continue;
43: }
44:
45: if (field.Type == SPFieldType.Computed)
46: {
47: Console.WriteLine("Field '{0}' is a computed column and will not be updated.", field.InternalName);
48: continue;
49: }
50:
51: if (field.Type == SPFieldType.URL)
52: item[field.Id] = new SPFieldUrlValue(fieldData);
53: else if (field.Type == SPFieldType.User)
54: AddListItem.SetUserField(web, item, field, fieldData);
55: else
56: item[field.Id] = fieldData;
57: }
58: catch (ArgumentException)
59: {
60: Console.WriteLine("WARNING: Could not set field {0} for item {1}.", fieldName, item.ID);
61: }
62: }
63:
64: page.Update();
65: }
66: }
The help for the command is shown below:
C:\>stsadm -help gl-createpublishingpage
stsadm -o gl-createpublishingpage
Creates a new publishing page.
Parameters:
-url <url to the publishing web within which to create the page>
-name <the filename of the page to create (do not include the extension)>
-title <the page title>
-layout <the filename of the page layout to use>
[-fielddata <semi-colon separated list of key value pairs: "Field1=Val1;Field2=Val2"> (use ';;' to escape semi-colons in data values)]
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-createpublishingpage | MOSS 2007 | Released: 9/4/2008
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL to the publishing web within which to create the page. | -url http://portal | |
| name | n | Yes | The filename of the page to create. It is not necessary to include the extension. Do not use special characters. | -name NewPage
-n NewPage |
| title | t | Yes | The title of the page. Wrap within quotes if includes spaces. | -title "New Page"
-t "New Page" |
| layout | l | Yes | The filename of the page layout to use. | -layout DefaultLayout.aspx
-l DefaultLayout.aspx |
| fielddata | fd | No | Key/Value pairs of metadata to apply to the new page. Use the internal field name and separate multiple pairs with a semi-colon. Use the following format when setting data: "Field1=Val1;Field2=Val2". | -fielddata "PublishingContactEmail=user@domain.com;PublishingContactName=First Last"
-fd "PublishingContactEmail=user@domain.com;PublishingContactName=First Last" |
The following is an example of how to add a new publishing page:
stsadm -o gl-createpublishingpage -url http://portal -name NewPage -title "New Page" -layout DefaultLayout.aspx
Enumerating Email Enabled Lists via STSADM
I can't really take much credit for this command - fellow MVP Todd Klindt had a custom console application that he'd created to do the same thing but it was lacking some functionality and was not created as an stsadm extension. He asked if I'd mind reworking it to allow different scopes and to make it an extension. Fortunately I had some time so I took a look at his code to get this one started. In the end I only ended up using one line of code from Todd's version - the code to get the email suffix (the part after the "@"). To get the suffix you use the following code:
m_emailSuffix = SPFarm.Local.GetChild<SPIncomingEmailService>().ServerDisplayAddress.ToLower();
Having this saved me a lot of time though as I had no idea how to retrieve this information (though some reflection would have eventually revealed it to me). The rest of it I had to recreate because we wanted to add the ability to use different scopes and because I wanted it to have the results output as XML (because XML is cool) and I figured I might as well dump out the other details related to the email configuration, not just the email alias. To handle the scope requirement I used the SPEnumerator class that I built a while back - made it real easy as all I had to do was listen to the list enumerated event and initialize the object with the right starting place.
I thought about making the XML results hierarchical but in the end decided that a flat list was sufficient as it accomplished the main goal. The complete code can be seen below - you can see an example of the output at the bottom of this post:
1: using System;
2: using System.Collections.Generic;
3: using System.Collections.Specialized;
4: using System.IO;
5: using System.Text;
6: using System.Xml;
7: using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
8: using Lapointe.SharePoint.STSADM.Commands.SPValidators;
9: using Microsoft.SharePoint;
10: using Microsoft.SharePoint.Administration;
11:
12: namespace Lapointe.SharePoint.STSADM.Commands.Lists
13: {
14: public class EnumEmailEnabledLists : SPOperation
15: {
16: private static XmlTextWriter xmlWriter;
17: private static string m_emailSuffix;
18:
19: /// <summary>
20: /// Initializes a new instance of the <see cref="EnumEmailEnabledLists"/> class.
21: /// </summary>
22: public EnumEmailEnabledLists()
23: {
24: SPParamCollection parameters = new SPParamCollection();
25: parameters.Add(new SPParam("url", "url", false, null, new SPUrlValidator()));
26: parameters.Add(new SPParam("scope", "s", false, "site", new SPRegexValidator("(?i:^Farm$|^WebApplication$|^Site$|^Web$)")));
27: parameters.Add(new SPParam("output", "output", false, null, new SPDirectoryExistsAndValidFileNameValidator()));
28:
29: StringBuilder sb = new StringBuilder();
30: sb.Append("\r\n\r\nOutputs the lists that have been email enabled.\r\n\r\nParameters:");
31: sb.Append("\r\n\t[-scope <Farm | WebApplication | Site | Web>]");
32: sb.Append("\r\n\t[-url <url of web application, site, or web to iterate)>]");
33: sb.Append("\r\n\t[-output <file to save the results to>]");
34:
35: Init(parameters, sb.ToString());
36: }
37:
38: #region ISPStsadmCommand Members
39:
40: /// <summary>
41: /// Gets the help message.
42: /// </summary>
43: /// <param name="command">The command.</param>
44: /// <returns></returns>
45: public override string GetHelpMessage(string command)
46: {
47: return HelpMessage;
48: }
49:
50: /// <summary>
51: /// Runs the specified command.
52: /// </summary>
53: /// <param name="command">The command.</param>
54: /// <param name="keyValues">The key values.</param>
55: /// <param name="output">The output.</param>
56: /// <returns></returns>
57: public override int Execute(string command, StringDictionary keyValues, out string output)
58: {
59: output = string.Empty;
60: string url = null;
61: if (Params["url"].UserTypedIn)
62: {
63: url = Params["url"].Value.TrimEnd('/');
64: }
65: string scope = Params["scope"].Value.ToLowerInvariant();
66: string xml = GetEmailEnabledLists(scope, url);
67:
68: if (Params["output"].UserTypedIn)
69: {
70: File.WriteAllText(Params["output"].Value, xml);
71: }
72: else
73: Console.WriteLine(xml);
74:
75: return OUTPUT_SUCCESS;
76: }
77:
78:
79: /// <summary>
80: /// Validates the specified key values.
81: /// </summary>
82: /// <param name="keyValues">The key values.</param>
83: public override void Validate(StringDictionary keyValues)
84: {
85: Params["url"].IsRequired = (Params["scope"].Value.ToLowerInvariant() != "farm");
86:
87: base.Validate(keyValues);
88: }
89:
90: #endregion
91:
92: /// <summary>
93: /// Gets the email enabled lists.
94: /// </summary>
95: /// <param name="scope">The scope.</param>
96: /// <param name="url">The URL.</param>
97: /// <returns></returns>
98: public static string GetEmailEnabledLists(string scope, string url)
99: {
100: m_emailSuffix = SPFarm.Local.GetChild<SPIncomingEmailService>().ServerDisplayAddress.ToLower();
101:
102: SPEnumerator enumerator;
103: if (scope == "farm")
104: {
105: enumerator = new SPEnumerator(SPFarm.Local);
106: }
107: else if (scope == "webapplication")
108: {
109: enumerator = new SPEnumerator(SPWebApplication.Lookup(new Uri(url)));
110: }
111: else if (scope == "site")
112: {
113: using (SPSite site = new SPSite(url))
114: {
115: enumerator = new SPEnumerator(site);
116: }
117: }
118: else
119: {
120: using (SPSite site = new SPSite(url))
121: using (SPWeb web = site.AllWebs[Utilities.GetServerRelUrlFromFullUrl(url)])
122: {
123: enumerator = new SPEnumerator(web);
124: }
125: }
126:
127: enumerator.SPListEnumerated += new SPEnumerator.SPListEnumeratedEventHandler(enumerator_SPListEnumerated);
128:
129: StringBuilder sb = new StringBuilder();
130:
131: xmlWriter = new XmlTextWriter(new StringWriter(sb));
132: xmlWriter.Formatting = Formatting.Indented;
133:
134: xmlWriter.WriteStartElement("Lists");
135:
136: enumerator.Enumerate();
137:
138: xmlWriter.WriteEndElement();
139: xmlWriter.Flush();
140: return sb.ToString();
141: }
142:
143: /// <summary>
144: /// Handles the SPListEnumerated event of the enumerator control.
145: /// </summary>
146: /// <param name="sender">The source of the event.</param>
147: /// <param name="e">The <see cref="Lapointe.SharePoint.STSADM.Commands.OperationHelpers.SPEnumerator.SPListEventArgs"/> instance containing the event data.</param>
148: private static void enumerator_SPListEnumerated(object sender, SPEnumerator.SPListEventArgs e)
149: {
150: if (e.List.EmailAlias != null)
151: {
152: xmlWriter.WriteStartElement("List");
153: xmlWriter.WriteAttributeString("siteUrl", e.Site.Url);
154: xmlWriter.WriteAttributeString("webUrl", e.Web.ServerRelativeUrl);
155: xmlWriter.WriteAttributeString("listUrl", e.List.RootFolder.Url);
156: xmlWriter.WriteAttributeString("alias", e.List.EmailAlias + "@" + m_emailSuffix);
157:
158: //Group attachments in folders, options: "subject"/"sender"/"root"
159: if (e.List.RootFolder.Properties["vti_emailattachmentfolders"] != null)
160: xmlWriter.WriteAttributeString("groupAttachmentsBy", e.List.RootFolder.Properties["vti_emailattachmentfolders"].ToString());
161:
162: //Overwrite files with the same name, options 1/0
163: if (e.List.RootFolder.Properties["vti_emailoverwrite"] != null)
164: xmlWriter.WriteAttributeString("overwriteExisting", (e.List.RootFolder.Properties["vti_emailoverwrite"].ToString() == "1"?"Yes":"No"));
165:
166: //Save original e-mail, options 1/0
167: if (e.List.RootFolder.Properties["vti_emailsaveoriginal"] != null)
168: xmlWriter.WriteAttributeString("saveOriginal", (e.List.RootFolder.Properties["vti_emailsaveoriginal"].ToString()=="1"?"Yes":"No"));
169:
170: //Save meeting invitations, options 1/0
171: if (e.List.RootFolder.Properties["vti_emailsavemeetings"] != null)
172: xmlWriter.WriteAttributeString("saveMeetingInvitations", (e.List.RootFolder.Properties["vti_emailsavemeetings"].ToString()=="1"?"Yes":"No"));
173:
174: //Email Security Policy, options 1/0
175: if (e.List.RootFolder.Properties["vti_emailusesecurity"] != null)
176: xmlWriter.WriteAttributeString("securityPolicy", (e.List.RootFolder.Properties["vti_emailusesecurity"].ToString() == "1"?"UseDocLibPermissions":"AcceptFromAnySender"));
177:
178:
179: xmlWriter.WriteEndElement();
180: }
181: }
182:
183: }
184: }
The help for the command is shown below:
C:\>stsadm -help gl-enumemailenabledlists
stsadm -o gl-enumemailenabledlists
Outputs the lists that have been email enabled.
Parameters:
[-scope <Farm | WebApplication | Site | Web>]
[-url <url of web application, site, or web to iterate)>]
[-output <file to save the results to>]
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-enumemailenabledlists | WSS v3, MOSS 2007 | Released: 8/26/2008
|
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| scope | s | No | One of the following values: Farm, WebApplication, Site, Web. The command will recursively loop through each item contained within the specified scope level. If omitted then defaults to "site". | -scope site
-s site |
| url | Yes, unless the scope is Farm | The URL to the web application, site collection, or web per the scope setting. | -url http://portal | |
| output | No | The output path to save the results to. Must be a valid filename. If not specified then the results will be displayed in the console. | -output "c:\lists.xml" |
The following is an example of how to get the list of email enabled lists:
stsadm -o gl-enumemailenabledlists -scope site -url http://portal -output c:\lists.xml
The results of running the above command will look something like the following:
<Lists> <List siteUrl="http://portal" webUrl="/" listUrl="Documents" alias="test1@domain.com" groupAttachmentsBy="root" overwriteExisting="No" saveOriginal="No" saveMeetingInvitations="No" securityPolicy="UseDocLibPermissions" /> <List siteUrl="http://portal" webUrl="/SubSite1" listUrl="Lists/Announcements" alias="test3@domain.com" saveOriginal="No" saveMeetingInvitations="No" securityPolicy="UseDocLibPermissions" /> <List siteUrl="http://portal" webUrl="/SubSite1" listUrl="Documents" alias="test2@domain.com" groupAttachmentsBy="subject" overwriteExisting="Yes" saveOriginal="Yes" saveMeetingInvitations="Yes" securityPolicy="AcceptFromAnySender" /> </Lists>
Enabling Audience Targeting on a List
I thought I was done documenting my audience related STSADM commands and then I realized that I had completely forgotten one. The first audience related command I had created was to enable audience targeting on a list - I called it gl-listaudiencetargeting. I needed this because my current project had tons of lists that that needed audience targeting turned on. Eventually these lists were added via a Feature but initially I did it via the command line with STSADM so that we could get a demo put together. The nice thing is that adding the code to the Feature became a no-brainer because the code was already tested so it became simple copy and paste.
So, how do you programmatically enable audience targeting? When I first started on this I thought it would be easy as setting a property on the list because enabling it via the browser is as simple as setting a checkbox but turns out it's not quite that simple. The way audience targeting works is that a "special" field is added to the list. The field schema looks like this:
<Field ID="61cbb965-1e04-4273-b658-eedaa662f48d" Type="TargetTo" Name="TargetTo" DisplayName="Target Audience" Required="FALSE" />
Once we have this schema we can add the field to the list using the AddFieldAsXml method (member of the SPFieldCollection class which can be obtained via the lists Fields property):
1: /// <summary>
2: /// Sets the whether audience targeting is enabled or not.
3: /// </summary>
4: /// <param name="url">The URL.</param>
5: /// <param name="enabled">if set to <c>true</c> [enabled].</param>
6: public static void SetTargeting(string url, bool enabled)
7: {
8: using (SPSite site = new SPSite(url))
9: using (SPWeb web = site.OpenWeb())
10: {
11: SPList list = Utilities.GetListFromViewUrl(web, url);
12:
13: if (list == null)
14: throw new SPException("List was not found.");
15:
16: SPField targetingField = GetTargetingField(list);
17: if (enabled && (targetingField == null))
18: {
19: string createFieldAsXml = CreateFieldAsXml();
20: list.Fields.AddFieldAsXml(createFieldAsXml);
21: list.Update();
22: }
23: else if (!enabled && (targetingField != null))
24: {
25: list.Fields.Delete(targetingField.InternalName);
26: list.Update();
27: }
28:
29: }
30: }
31:
32: /// <summary>
33: /// Gets the targeting field.
34: /// </summary>
35: /// <param name="list">The list.</param>
36: /// <returns></returns>
37: private static SPField GetTargetingField(SPList list)
38: {
39: SPField field = null;
40: try
41: {
42: field = list.Fields[new Guid("61cbb965-1e04-4273-b658-eedaa662f48d")];
43: }
44: catch (ArgumentException)
45: {
46: }
47: return field;
48: }
49:
50: /// <summary>
51: /// Gets the field as XML.
52: /// </summary>
53: /// <returns></returns>
54: private static string CreateFieldAsXml()
55: {
56: XmlElement element = new XmlDocument().CreateElement("Field");
57: element.SetAttribute("ID", "61cbb965-1e04-4273-b658-eedaa662f48d");
58: element.SetAttribute("Type", "TargetTo");
59: element.SetAttribute("Name", "TargetTo");
60: element.SetAttribute("DisplayName", "Target Audiences");
61: element.SetAttribute("Required", "FALSE");
62: return element.OuterXml;
63: }
The help for the command is shown below:
C:\>stsadm -help gl-listaudiencetargeting
stsadm -o gl-listaudiencetargeting
Enabling audience targeting will create a targeting column for the list. Web parts, such as the Content Query Web Part, can use this data to filter list contents based on the user's context.
Parameters:
-url <list view url>
-enabled <true|false>
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-listaudiencetargeting | MOSS 2007 | Released: 8/6/2008 |
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | url | Yes | The URL to the list that you wish to enable audience targeting on. This can be the URL to the root folder or to a specific list view. | -url http://portal/lists/announcements
or -url http://portal/lists/announcements/forms/allitems.aspx |
| enabled | e | Yes | "true" to enable audience targeting, "false" to disable audience targeting. Note that disabling audience targeting deletes the "Target Audience" field and therefore any data that may have been assigned to the field. | -enabled true |
The following is an example of how to enable audience targeting for an announcements list:
stsadm -o gl-listaudiencetargeting -url http://portal/lists/announcements -enabled true
Setting List Content Types using STSADM
Sometimes when I'm working on a new Feature I find it easier to take certain snippets that I need to test and pull them out into custom STSADM commands. This enables me to quickly and easily test the core code and without having to go through all the deployment steps. It was for this purpose that I originally created this new command, gl-setlistcontenttypes. I did find later that the command ended up being real useful in a scripted deployment that I'm working on so I managed to knock out a well tested new command and some reusable code for a Feature that I'm working on.
The code is really simple - I've got a primary method, SetContentTypes, which takes in 3 string arrays containing the content types to remove and add as well as another array for the new button order. There's also a couple of helper methods that are called. I then just loop through each array and call the necessary methods (Add or Delete) on the ContentTypes property which returns an SPContentTypeCollection object. The only tricky part is setting the order of the content types - this is done via RootFolder property. There's a UniqueContentTypeOrder property that must be set to an IList<SPContentType> object. Manipulating the IList object directly will not work as it does not set the dirty flag and therefore the Update() method will appear to do nothing so you must create a new object and reset the property:
1: /// <summary>
2: /// Sets the content types.
3: /// </summary>
4: /// <param name="web">The web.</param>
5: /// <param name="list">The list.</param>
6: /// <param name="contentTypesToAdd">The content types to add.</param>
7: /// <param name="contentTypesToDelete">The content types to delete.</param>
8: /// <param name="contentTypeOrder">The content type order.</param>
9: public static void SetContentTypes(SPWeb web, SPList list, string[] contentTypesToAdd, string[] contentTypesToDelete, string[] contentTypeOrder)
10: {
11: foreach (string ct in contentTypesToAdd)
12: {
13: AddContentTypeToList(web, list, ct.Trim());
14: }
15: foreach (string ct in contentTypesToDelete)
16: {
17: foreach (SPContentType contentType in list.ContentTypes)
18: {
19: if (contentType.Name.ToLowerInvariant() == ct.Trim().ToLowerInvariant())
20: {
21: list.ContentTypes.Delete(contentType.Id);
22: break;
23: }
24: }
25: }
26: ChangeContentTypeOrder(list, contentTypeOrder);
27: }
28:
29: /// <summary>
30: /// Changes the content type order.
31: /// </summary>
32: /// <param name="list">The list.</param>
33: /// <param name="contentTypes">The content types.</param>
34: public static void ChangeContentTypeOrder(SPList list, string[] contentTypes)
35: {
36: if (contentTypes.Length == 0)
37: return;
38:
39: IList<SPContentType> contentTypeOrder = new List<SPContentType>();
40: foreach (string contentTypeName in contentTypes)
41: {
42: SPContentType contentType = null;
43: try
44: {
45: contentType = list.ContentTypes[contentTypeName.Trim()];
46: }
47: catch (ArgumentException)
48: {
49: Log("WARNING: Unable to set content type order for '{0}'. Content type was not found.",
50: contentTypeName);
51: }
52: if (contentType != null)
53: contentTypeOrder.Add(contentType);
54: else
55: Log("WARNING: Unable to set content type order for '{0}'. Content type was not found.",
56: contentTypeName);
57: }
58: list.RootFolder.UniqueContentTypeOrder = contentTypeOrder;
59: list.RootFolder.Update();
60:
61: }
62:
63:
64: /// <summary>
65: /// Adds the content type to list.
66: /// </summary>
67: /// <param name="web">The web.</param>
68: /// <param name="list">The list.</param>
69: /// <param name="contentTypeName">Name of the content type.</param>
70: /// <returns></returns>
71: public static SPContentType AddContentTypeToList(SPWeb web, SPList list, string contentTypeName)
72: {
73: SPContentType contentType = null;
74: try
75: {
76: contentType = list.ContentTypes[contentTypeName];
77: }
78: catch (ArgumentException)
79: { }
80: if (contentType == null)
81: {
82: try
83: {
84: // Get the content type from the web and add to the list.
85: contentType = web.ContentTypes[contentTypeName];
86: }
87: catch (ArgumentException)
88: {
89: }
90: if (contentType == null)
91: throw new SPException(string.Format("Unable to find content type '{0}'", contentTypeName));
92:
93: return list.ContentTypes.Add(contentType);
94: }
95: return null;
96: }
The help for the command is shown below:
C:\>stsadm -help gl-setlistcontenttypes
stsadm -o gl-setlistcontenttypes
Adds or removes content typews associated with the given list.
Parameters:
-url <list view url>
[-add <comma separated list of content type names to add to the list>]
[-remove <comma separated list of content type names to remove from the list>]
[-order <new button order, comma separated, first will be the default>]
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-setlistcontenttypes | WSS v3, MOSS 2007 | 8/6/2008 |
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL to the list of which the actions will be performed | -url http://portal/Lists/Events | |
| add | a | No | A comma separated list of content type names to add to the list. It's recommended to wrap the value in quotes if more than one. | -add "Holiday,Vacation,Company Event"
-a "Holiday,Vacation,Company Event" |
| remove | r | No | A comma separated list of content type names to remove from the list. It's recommended to wrap the value in quotes if more than one. | -remove Event
-r Event |
| order | No | A comma separated list of content type names. Sets the order of the items in the "New" button of the list. The first item will be the default content type. | -order "Vacation,Holiday,Company Event" |
The following is an example of how to set the content types for a list:
stsadm -o gl-setlistcontenttypes -url http://portal/Lists/Events -add "Holiday,Vacation,Company Events" -remove "Event" -order "Vacation,Holiday,Company Event"
Adding a List View Web Part to a Page
I had a requirement recently to show a view of a list from a child site on the page of a parent site. You can of course do this easily with a content query web part and then just manipulate the XSL to get the output to look the way you want but what if you want to be able to use the out of the box List View Web Part so that you get the toolbar and all the other stuff that the web part provides? Many people aren't even aware that this is possible but it is - unfortunately it's kind of a pain to do via the browser - you have to add the web part to a page, export it using a custom tool or code (not sure if SPD allows you to export but you can't do it via the browser), edit the resultant XML to set the WebId attribute, and then import the web part to the desired location.
To get around all these steps I built a simple command called gl-addlistviewwebpart. I already had a command, gl-setwebpartstate, that could accomplish the same thing but using that command would require that you know the WebId and ListId which would most likely not be available during a scripted install.
The majority of the code is a derivative of the code from the gl-setwebpartstate command. I simply instantiate a ListViewWebPart object, set a few properties and then add the web part using an instance of the SPLimitedWebPartManager class:
1: /// <summary>
2: /// Adds a List View Web Part to the specified page.
3: /// </summary>
4: /// <param name="pageUrl">The page URL.</param>
5: /// <param name="listUrl">The list URL.</param>
6: /// <param name="title">The title.</param>
7: /// <param name="zoneID">The zone ID.</param>
8: /// <param name="zoneIndex">Index within the zone.</param>
9: /// <param name="publish">if set to <c>true</c> [publish].</param>
10: public static void Add(string pageUrl, string listUrl, string title, string zoneID, int zoneIndex, bool publish)
11: {
12: using (SPSite site = new SPSite(pageUrl))
13: using (SPWeb web = site.OpenWeb())
14: // The url contains a filename so AllWebs[] will not work unless we want to try and parse which we don't
15: {
16: SPFile file = web.GetFile(pageUrl);
17:
18: // file.Item will throw "The object specified does not belong to a list." if the url passed
19: // does not correspond to a file in a list.
20:
21: if (file.InDocumentLibrary)
22: {
23: if (!Utilities.IsCheckedOut(file.Item) || !Utilities.IsCheckedOutByCurrentUser(file.Item))
24: file.CheckOut();
25: // If it's checked out by another user then this will throw an informative exception so let it do so.
26: }
27:
28: string displayTitle = string.Empty;
29: ListViewWebPart lvw = null;
30: SPLimitedWebPartManager manager = null;
31: try
32: {
33: manager = web.GetLimitedWebPartManager(pageUrl, PersonalizationScope.Shared);
34: lvw = new ListViewWebPart();
35: SPList list = Utilities.GetListFromViewUrl(listUrl);
36: if (list == null)
37: throw new ArgumentException("List not found.");
38:
39: lvw.ListName = list.ID.ToString("B").ToUpperInvariant();
40: lvw.TitleUrl = list.DefaultViewUrl;
41: lvw.WebId = list.ParentWeb.ID;
42:
43: if (!string.IsNullOrEmpty(title))
44: lvw.Title = title;
45:
46:
47: displayTitle = lvw.DisplayTitle;
48:
49: manager.AddWebPart(lvw, zoneID, zoneIndex);
50: }
51: finally
52: {
53: if (manager != null)
54: {
55: manager.Web.Dispose();
56: manager.Dispose();
57: }
58: if (lvw != null)
59: lvw.Dispose();
60:
61:
62: if (file.InDocumentLibrary && Utilities.IsCheckedOut(file.Item))
63: file.CheckIn("Checking in changes to page due to new web part being added: " + displayTitle);
64:
65: if (publish && file.InDocumentLibrary)
66: {
67: file.Publish("Publishing changes to page due to new web part being added: " + displayTitle);
68: if (file.Item.ModerationInformation != null)
69: {
70: file.Approve("Approving changes to page due to new web part being added: " + displayTitle);
71: }
72: }
73: }
74: }
75: }
The help for the command is shown below:
C:\>stsadm -help gl-addlistviewwebpart
stsadm -o gl-addlistviewwebpart
Adds a list view web part to a page.
Parameters:
-url <web part page URL>
-listurl <list url>
-zone <zone ID>
-zoneindex <zone index>
[-title <web part title>]
[-publish]
|
The following table summarizes the command and its various parameters:
| Command Name | Availability | Build Date |
|---|---|---|
| gl-addlistviewwebpart | WSS v3, MOSS 2007 | 8/6/2008 |
| Parameter Name | Short Form | Required | Description | Example Usage |
|---|---|---|---|---|
| url | Yes | The URL to the page to add the web part to. | -url http://portal/pages/default.aspx | |
| listurl | lu | Yes | The URL of the list to point to. | -listurl http://portal/subsite/Lists/Events
-lu http://portal/subsite/Lists/Events |
| zone | z | Yes | The web part zone to add the web part to. | -zone RightColumnZone
-z RightColumnZone |
| zoneindex | zi | Yes | The index within the zone to add the web part. | -zoneindex 1
-zi 1 |
| title | t | No | The title to give the web part. If not specified then the list title is used. | -title Events
-t Events |
| publish | p | No | Publishes the page, if supported. The page will be automatically checked-in to share draft - passing in this parameter will also publish the page. |
The following is an example of how to add a list view web part to a page where the list is contained within a sub site:
stsadm -o addlistviewwebpart -url http://portal/pages/default.aspx -listurl http://portal/subsite/lists/events -zone RighColumnZone -zoneindex 1 -title "Company Events" -publish
Check out the books I've contributed to at Amazon.com: